Skip to content

Declare the generator pin once and record what the theme is a copy of - #30

Merged
ptr727 merged 3 commits into
developfrom
feature/type-conformance-fixes
Aug 5, 2026
Merged

Declare the generator pin once and record what the theme is a copy of#30
ptr727 merged 3 commits into
developfrom
feature/type-conformance-fixes

Conversation

@ptr727

@ptr727 ptr727 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Closes #28. Closes #29.

Conformance gaps against the fleet hugo type landing in ptr727/ProjectTemplate#560, plus two smaller items found alongside them. #27 was filed in the same pass and is closed as invalid — the premise was wrong and this repo's design was right, explained in that thread.

The generator pin was declared twice

HUGO_VERSION and HUGO_SHA256 lived in both validate-task.yml and deploy-site-task.yml, each with an "update both values together" instruction and nothing enforcing it.

A one-sided bump was silent, and produced exactly the failure the pin exists to prevent: validation building the site with one generator while the deploy shipped a tree built by another, each install verifying its own checksum against its own version and both passing. No Dependabot ecosystem tracks Hugo, so both values move by hand and there was no bot to catch the skew.

Both installs now call .github/actions/install-hugo, which owns the pin. The two cannot diverge because there is only one declaration. It also asserts the extended build from hugo version rather than inferring it from the .deb file name, which is the only thing that carried that requirement before (deploy/README.md:24 states it as a requirement).

Verified: grep -rn 'HUGO_VERSION|HUGO_SHA256' .github/ returns exactly one file.

The theme recorded no upstream ref

themes/PaperMod is 125 tracked files with no .gitmodules, no version marker, and no recorded origin, so nothing could be diffed against upstream, checked for a fix, or updated with any confidence.

themes/README.md now records it. The commit was recovered rather than guessed, by hashing all 125 tracked blobs and matching them against upstream history:

Commit 154d006e0182dfc7da38008323976b02e6bfab4a
Committed 2026-05-10
Describes as v8.0-138-g154d006

Every file matches that commit exactly except two, and both are additions in extension points the theme documents for the purpose rather than forks of theme logic:

File Edit
assets/css/extended/blank.css ships empty; carries the Lexend body font and the gallery-cols-* rules
layouts/_partials/extend_head.html ships empty; carries the Google Fonts links for Lexend

The record notes both could live at the project root instead, since Hugo resolves a project's own assets/css/extended/ and layouts/_partials/ ahead of the theme's. Moving them would make the next update a clean directory replace with nothing to reapply. Left for that update rather than done here.

It also notes what this now makes answerable: TODO.md records that PaperMod uses APIs Hugo deprecated in 0.158, which is why two layouts/ overrides exist to keep --panicOnWarning usable. Whether they are still needed is a diff against the recorded commit, which was not previously possible.

The record sits at themes/README.md, outside PaperMod/, so replacing that directory on an update does not take it with it.

A scoping bug that fix exposed

The markdown glob excluded !themes/**, so the provenance file — authored here, about a vendored tree — would not have been linted. It now excludes !themes/*/**, reaching inside a theme directory rather than over the directory that holds them.

Verified: 16 files linted, 0 issues, with themes/README.md in scope and themes/PaperMod/README.md still out.

Two smaller items

  • permissions: {} on assert-ref and assert-environment. Both only echo and case-match, and both were inheriting the repository default token scope. Not exploitable, but it is the same least-privilege finding the hub has open against merge-bot in merge-bot: GITHUB_TOKEN permissions are unused, since every write goes through the App token ProjectTemplate#521.
  • outputs: on deploy-site-task.ymlrelease-id and site-url. There were none, so no caller could record what shipped. The live check already proves that id is the one answering, which makes it the value a rollback names.

Verification

Gate Result
actionlint clean, exit 0
markdownlint-cli2 0 issues in 16 files
editorconfig-checker clean on everything tracked (remaining hits are gitignored public/)
hugo --gc --minify --panicOnWarning exit 0

Correction: an earlier revision of this description said the deploy path itself is unchanged. That was true when written and stopped being true two commits later. fa0fc19 adds StrictHostKeyChecking=yes, UserKnownHostsFile and BatchMode=yes to the rsync transport, which is the live deploy path, and moves the production ref gate to the full ref. Those were verified locally with ssh -G but have never run against the real host through rrsync -wo. The transport now fails closed, so a stale DEPLOY_SSH_KNOWN_HOSTS that was previously tolerated will stop a deploy. Staging must pass before production, and the retest is tracked in #33 for the Blog and VPS agents to run together.

🤖 Generated with Claude Code

Three conformance gaps against the fleet hugo type
(ptr727/ProjectTemplate#560), plus two smaller items found alongside them.

Closes #28. Closes #29.

The Hugo version and checksum were declared in both validate-task.yml and
deploy-site-task.yml, each with an instruction to update both and nothing
enforcing it. A one-sided bump was silent and produced the failure the pin
exists to prevent: validation building the site with one generator while the
deploy shipped a tree built by another, each verifying its own checksum
against its own version and both passing. No Dependabot ecosystem tracks
Hugo, so there was no bot to catch the skew either. Both installs now call a
composite action that owns the pin, so the two cannot diverge, and it
asserts the extended build from the binary rather than inferring it from the
file name.

The vendored theme recorded no upstream ref, so nothing could be diffed,
updated, or audited against it. themes/README.md now records the commit,
recovered by matching all 125 tracked blobs against upstream history rather
than guessed: 154d006e0182dfc7da38008323976b02e6bfab4a, describing as
v8.0-138-g154d006. Every file matches it exactly except two, both additions
in extension points the theme documents for the purpose, and both are listed
with the note that Hugo would resolve them from the project root instead,
which would make the next update a clean directory replace. The record sits
outside PaperMod/ so replacing that directory does not take it with it.

That exposed a scoping bug: the markdown glob excluded all of themes/, so a
file we author about a vendored tree would not have been linted. It now
excludes themes/*/** instead, reaching inside a theme rather than over the
directory that holds them.

Also: assert-ref and assert-environment ran without a permissions block, so
two jobs that only echo and case-match inherited the repository default,
and both are now permissions: {}. And deploy-site-task.yml exposed no
outputs, so no caller could record what shipped; it now returns release-id
and site-url, and the live check already proves that id is the one
answering, which makes it the value a rollback names.

Verified: actionlint clean, markdownlint 0 issues across 16 files with the
provenance file now in scope and the vendored tree still out,
editorconfig-checker clean on everything tracked, and the site builds under
--panicOnWarning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR closes #28 and #29 by centralizing the Hugo generator pin into a single composite action and documenting the provenance of the vendored PaperMod theme, while tightening least-privilege permissions for assertion-only jobs.

Changes:

  • Add a reusable composite action (.github/actions/install-hugo) that installs the pinned Hugo extended build and verifies it by checksum.
  • Update validation and deploy workflows to use the shared Hugo installer and adjust markdownlint globs so themes/README.md is linted while vendored theme trees remain excluded.
  • Add provenance documentation for the vendored PaperMod theme and reduce token permissions for assertion jobs.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
themes/README.md New provenance/update record for vendored themes (PaperMod).
.github/workflows/validate-task.yml Switch Hugo install to the composite action; adjust markdownlint globs to lint themes/README.md but exclude theme trees.
.github/workflows/deploy-site.yml Set permissions: {} on assert-ref job (no repo access needed).
.github/workflows/deploy-site-task.yml Add workflow outputs and job outputs; set permissions: {} on assert-environment; switch Hugo install to the composite action.
.github/actions/install-hugo/action.yml New composite action that installs and asserts Hugo extended, pinned by version+SHA256.
Suppressed comments (1)

.github/workflows/deploy-site-task.yml:18

  • GitHub Actions expressions can’t access hyphenated output names via dot notation (this parses like subtraction). Use bracket notation when referencing the job output key site-url.
        value: ${{ jobs.deploy.outputs.site-url }}

Comment thread .github/workflows/deploy-site-task.yml
@ptr727

ptr727 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed review comment

It carries no thread, so quoting it here. From the collapsed low-confidence block on the current review, against .github/workflows/deploy-site-task.yml:18:

GitHub Actions expressions can't access hyphenated output names via dot notation (this parses like subtraction). Use bracket notation when referencing the job output key site-url.

        value: ${{ jobs.deploy.outputs.site-url }}

Declining, for the same reason as the threaded copy of this finding against line 15, answered in full there. In short: hyphens are legal in Actions property dereference (the name must start with a letter or _, then may contain letters, digits, -, or _), the hub's canonical build-pypilibrary-task.yml uses exactly this construct and has shipped every PyPI publish in the fleet through it, and actionlint passes on this branch at exit 0 while specifically type-checking context property access.

Both instances stay as they are.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/actions/install-hugo/action.yml:36

  • This action is intended to be the single source of truth for the Hugo pin, but exposing version/sha256 as overridable inputs allows callers to pass different values in different workflows and reintroduce the same silent divergence risk this refactor is meant to eliminate. Hardcode the pin inside the action so workflows cannot override it.
inputs:
  version:
    description: Hugo version to install.
    required: false
    default: 0.164.0

Copilot's point, and it is right: exposing version and sha256 as inputs with
defaults left the divergence this action exists to remove, one level up. Two
callers could pass different values and reintroduce the silent skew, and the
only thing preventing it was that neither caller passes the arguments today.

That is correctness by convention, which is what the original two-file pin
also was. The pin is now hardcoded in the action, so callers cannot override
it and every caller moves together or none does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 03:04
@ptr727

ptr727 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Answering the suppressed comment from round 2

No thread, so quoting it. Against .github/actions/install-hugo/action.yml:36:

This action is intended to be the single source of truth for the Hugo pin, but exposing version/sha256 as overridable inputs allows callers to pass different values in different workflows and reintroduce the same silent divergence risk this refactor is meant to eliminate. Hardcode the pin inside the action so workflows cannot override it.

Right, and taken in d531aafdf9843b645fd102189225c05b781fcdda.

This is the defect the pull request is about, moved up one level rather than removed. Two callers could have passed different values and produced exactly the skew described in #29: validation building with one generator while the deploy ships a tree built by another. The only thing preventing it was that neither caller passes the arguments today, which is correctness by convention, which is what the original two-file pin also was. Replacing one convention with another is not the fix I claimed to have made.

The pin is now hardcoded in the action's step env, the inputs are gone, and a comment records why an input would be wrong here so the next reader does not add one back as a convenience. Verified: grep -rn '0.164.0|8325f36' .github/ returns that one file and nothing else, and actionlint is clean at exit 0.

Worth noting this is the second finding on these two pull requests of the form "the safety here rests on an argument rather than on construction" — the other being a release id whose uniqueness depended on step ordering, in ptr727/ProjectTemplate#560. Both were worth taking.


The other suppressed comment on this round is the hyphenated-output-name claim against line 18, which is a repeat of round 1 and is already answered. Still declining it: hyphens are legal in Actions property dereference, the hub's canonical build-pypilibrary-task.yml uses the identical construct for every PyPI publish in the fleet, and actionlint type-checks context property access and passes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Two review findings from the hub's copy of this pipeline
(ptr727/ProjectTemplate#560), both of which apply here identically.

The production gate compared github.ref_name against "main". Tags and
branches are separate namespaces that share a short name, so a tag named
main would satisfy that comparison while pointing at an arbitrary commit,
bypassing the one gate protecting production. It now compares github.ref
against refs/heads/main, which is unambiguous.

The rsync transport left host key checking and the known-hosts location to
the runner's OpenSSH defaults. StrictHostKeyChecking defaults to ask, which
a non-interactive runner resolves ambiguously, and the known-hosts path was
the default rather than the file the deploy key step writes. Both are now
pinned, along with BatchMode=yes so a credential problem fails the step
instead of hanging the job to its timeout. The option string is declared
once at workflow level, so the upload and the pointer flip cannot drift
apart, which is the same single-declaration reasoning as the generator pin.

Verified: actionlint clean at exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 5, 2026 03:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

ptr727 added a commit to ptr727/ProjectTemplate that referenced this pull request Aug 5, 2026
Closes #456. Closes #558.

Blog has been running a Hugo build with an rsync-over-SSH deploy to a
host we own, and nothing governed it. `WORKFLOW.md`'s `Output Seam by
Destination` had no row for a filesystem on our own host,
`registry/repos.schema.json`'s `target` enum had no member for it, and
Blog stood cataloged as `source-only` behind two interim driftNotes
recording the deferral.

## What lands

**The `hugo` type** (`spec/project-types.json`), nine checks. Only
`hugo.build.strict` names the generator, where a generator-specific flag
*is* the letter. The rest are phrased generator-agnostically, so
promoting them to a shared type when a second generator arrives is a
registry edit rather than a rewrite. `spec/type-model.md` grows a
**Generators** section stating that rule and why there is no
`static-site` to `hugo` hierarchy at one member.

**The `self-hosted` target and the `deploy-ssh` mechanism.** What a repo
builds and where the result lands stay separate axes, so an SMB or S3
publisher later is a new *mechanism*, not a new type.

**D4.6 and D5.6.** D4.6 requires the deploy to assert *which release*
and *which environment* answered, polling to a bounded timeout, rather
than trusting the transport's exit status. D5.6 requires the prune of a
durable destination to be asserted on the host that was written to.

**A reference leaf pair** in `catalog/snippets/workflows/`, plus the 5A
addendum, scenarios S12/S13, and a section 6 walkthrough.

**Blog reclassified** to `["hugo", "source-only"]` with both publish
targets.

## Three questions a reviewer will ask

**Does D4.3 need extending?** No. It enumerates how a repo reaches the
tag-only release shape, and this destination adds no fourth route: the
deploy is a separate dispatch that touches no release, and Blog reaches
tag-only through the source-only route already listed.

**Why are `requires` and `stores` empty on `deploy-ssh`?** Not for want
of credentials. They are per-environment GitHub Environment secrets,
which neither `validate.py` nor `audit.py` can enumerate.
`validate.py:200` would force any `requires` name into the repo's
`requiredSecrets`, and `audit.py:615` unions that into the **actions**
store expectation, so listing them guarantees a false DEFECT on a
correctly configured repo. The new optional `environments` block records
the names as operator documentation and says plainly that it is not a
gate, so a clean audit is not evidence an environment is configured.
Extending the `stores` enum instead was measured and rejected:
`audit.py:606` seeds `required_by_store` with two keys and `:610`
indexes it unguarded, so an unknown store raises `KeyError` for every
repo whose `publish[]` maps to that mechanism. Follow-up folded into
`TODO.md`'s locally-required-secrets entry, since it is the same missing
axis.

**Why is the leaf concrete rather than parameterized?** An eleven-input
generalized transport leaf was proposed and rejected. The other twelve
leaves take `ref`/`branch`/`smoke` and nothing else; reuse is by
vendored copy, so a copier edits a `run:` line for free; the hub never
executes these files, so eleven parameters would be untested surface
presented as canonical; and naming it for the transport is the mistake
the type name avoids. The two outputs were kept, since Blog currently
has none and no caller can record what shipped.

## Two checks Blog fails today

Recorded as driftNotes naming their check id, so the next audit retires
them mechanically.

| Check | What is wrong | Filed |
| --- | --- | --- |
| `hugo.vendored.provenance` | The vendored theme is 125 tracked files
with no `.gitmodules`, no recorded upstream ref, and no Dependabot
ecosystem covering it. | ptr727/Blog#28 |
| `hugo.generator.pinned` | The generator version and checksum are
pinned in two workflows with nothing asserting they agree. |
ptr727/Blog#29 |

Both are fixed in ptr727/Blog#30.

## A third check was wrong, and the second commit fixes it

`hugo.deploy.retention` and D5.6 originally required the deploy to prune
the destination and assert the count on the host. **Blog cannot, and
should not be made to.** Its deploy credential is a forced `rsync`
command confined write-only, so the server never acts as sender and the
key can neither delete a release nor read the destination back to count
one. Blog's own ownership table already assigns release-prune timers to
the host, which is the correct resolution rather than a gap.

As first written the check was unsatisfiable for exactly the repos that
confine their credentials properly, and the only way to pass it was to
widen a deliberately narrow key. That trades a real confinement boundary
for a green check, so the check was wrong rather than the design.

Retention is now bounded by a **declared count with one side recorded as
owning the prune**: the deploy asserts it where its credential can
observe the destination, the host owns it where the credential cannot.
What the guarantee still rejects is a prune against a local scratch
tree, a best-effort prune, and neither side owning it, since each then
assumes the other prunes. The reference leaf keeps the
assert-in-pipeline shape and says when to delete the step. Blog passes
the corrected version, so its retention driftNote is dropped and
ptr727/Blog#27 is closed as invalid.

Worth noting as evidence for the type: a check written from the hub's
side alone, against a real repo, was wrong on first contact in a way
only the repo could reveal.

## The record was wrong, and is corrected rather than deleted

`TODO.md`'s intake entry predicted three things that are wrong against
what Blog actually runs, and writing the type from the prediction would
have encoded requirements the repo does not meet:

| Predicted | Measured |
| --- | --- |
| theme as a Dependabot-tracked submodule | vendored, no upstream ref
recorded |
| generator at `latest`, not pinned | pinned by version **and** SHA256 |
| tag cut last, after the live check | deploy is a separate dispatch;
the release is untouched |

## Drive-by fixes

- `spec/scope-model.md`'s project-type token table was missing `cpp`,
pre-existing.
- `STANDUP.md`'s new-type procedure never mentioned the registry
`target` enum, which is exactly what the first repo declaring a new
destination fails `validate.py` on. It also now warns that a leaf must
not be named `build-*-task.yml`, since `source-only.detect` is literally
that string.

## Verification

| Gate | Result |
| --- | --- |
| `spec/validate.py` | 22 cataloged, 0 backlog, clean |
| `spec/audit.py --selftest` | PASS |
| `scripts/repo_gate.py` | eol 0, sha-pin 0 |
| `scripts/prose_lint.py --diff develop` | clean (the 522-violation tree
backlog is #519's, untouched) |
| markdownlint-cli2 | 0 issues in 44 files |
| actionlint | clean |
| editorconfig-checker | only untracked `.artifacts/` and `Tests/obj/`
build output |

Selector resolution verified directly: Blog resolves to `{hugo,
source-only, release, dispatch-only, pull}`, the deploy leaf is
selected, `build-release-task.yml` is correctly **not** selected, the
`release` develop payload is selected over the operational one, and
`self-hosted` routes to `deploy-ssh` with an empty `requires` so no
secret finding is manufactured.

## Not in this PR

- The three Blog-side fixes, filed and fixed downstream.
- A fresh `reports/blog/audit.md`; the current one predates the deploy
and its staleness is noted in the conformance matrix.
- Real environment-scoped store support in `audit.py`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ptr727
ptr727 merged commit 49dfbbc into develop Aug 5, 2026
5 checks passed
@ptr727
ptr727 deleted the feature/type-conformance-fixes branch August 5, 2026 13:12
ptr727 added a commit to ptr727/ProjectTemplate that referenced this pull request Aug 5, 2026
…ate (#568)

Resolves the audit-tooling half of #563, filed from `ptr727/Blog` after
its re-integration. All four of that issue's asks, plus a defect in one
of the fixes it proposes.

## The gate that suppressed the check

`spec/audit.py` wrapped the whole freshness check in `if not findings:`.
The rationale was sound in isolation, in that a clean repo has no
outstanding work for a pending-marker note to describe, but the
consequence is that **any** repo carrying one finding it cannot clear
has its entire `driftNotes` list exempted. Blog carries exactly that,
the `carried: AGENTS.md references the template repo` finding tracked in
#552, so its notes were never checked however clean the rest of the
audit ran. The repo with open findings is where a stale note is most
likely, which is the inverse of what the gate produced.

Both shapes are now evaluated on every run, worded by context rather
than suppressed:

| Note | Audit clean | Findings open |
| --- | --- | --- |
| prose marker (`pending`, `still`, ...) | contradicted outright, as
before | raised as which of the open findings it means |
| names a check id | surfaced for a hand decision | surfaced for a hand
decision |

Measured live, this is one added advisory across the fleet. Of 53 notes
on 22 repos, one carries a marker (MediaTools, `pending fleet-wide
ratification`), and it now reads `while 25 finding(s) are open - confirm
it describes one of them rather than closed work`. Under the old gate it
was silent.

## The check-id matcher, and why it is not anchored

#563 proposes `\(([a-z]+\.[a-z.]+)\)$`. That pattern matches **neither
of the two notes it was written for**, because both end the sentence
after the paren:

> ... so it cannot be moved or diffed against upstream
(hugo.vendored.provenance)**.**

Run over the whole registry it matches zero notes on zero repos, which
is indistinguishable from a fleet carrying no such note. That is the
silent-narrowing shape `GOVERNANCE.md` "Verification Discipline" names:
a pattern that matches less still exits zero.

`CHECK_ID_RE` is therefore unanchored, and the self-test covers the
trailing-period case, the mid-sentence case, an id absent from the
catalog, an id whose type the repo does not declare, and a parenthesized
version string as the false positive the shape has to exclude. **Watched
failing**: re-anchoring the pattern to `$` turns five cases red and the
suite reports `SELFTEST FAIL`.

## What the audit will not claim

Resolving the id is as far as a tool can honestly go here, because
`spec/audit.py` never reads `spec/project-types.json` at all. So the
audit checks that the id exists and that the repo declares its type,
then hands the check itself to the auditor. The finding clears when the
note is deleted, which is the retirement path #560 promised, made
visible rather than left to a matcher that could never fire.

That same fact is now stated where an agent reads a run rather than only
in the issue: `AUDIT.md` section 4, the console line printed for a clean
repo, the `--issue` body preamble, and `OPERATIONS.md`. Adding a check
to `project-types.json` changes what an auditor must judge and changes
no tool's output, and silence from a tool that was never looking reads
exactly like a pass.

## Blog's two notes are dropped

Both deviations closed in ptr727/Blog#30 and are on Blog's ground-truth
`main` (`2b132e4`), verified by reading that branch rather than trusting
the issue:

- `hugo.vendored.provenance` - `themes/README.md` records the upstream
repository, commit `154d006e`, its upstream date, `git describe`, the
license location, and both local edits.
- `hugo.generator.pinned` - the version and SHA256 are declared once at
`.github/actions/install-hugo/action.yml:26-27`, and
`validate-task.yml:76` and `deploy-site-task.yml:79` both consume that
composite action.

## Verification

| Gate | Result |
| --- | --- |
| `spec/audit.py --selftest` | PASS, and FAIL on the anchored matcher |
| `spec/validate.py` | 22 cataloged, 0 backlog, clean |
| `spec/audit.py Blog MediaTools` (live) | behaves as described above |
| markdownlint-cli2 | 44 files, 0 issues |
| cspell (README, HISTORY) | 0 issues |
| editorconfig-checker | clean |
| `scripts/prose_lint.py` | no new violations on any touched file, net
-2 |

`Refs` rather than `Closes`, since a closing keyword cannot fire from a
`develop`-targeted pull request. #563 is closed by hand with evidence
once this merges.

Filed by an agent in `ptr727/Blog`, resolved here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants