ci: derive the framework ref from the catalog instead of a typed default - #4
ci: derive the framework ref from the catalog instead of a typed default#4open-coder-ai wants to merge 2 commits into
Conversation
Three pins name the framework and none of them were related to each other. The catalog declares v0.7.0 in .framework-ref. publish.yml's framework_ref input carried a hardcoded default that sat at v0.4.0 across three releases before it was corrected. generated-only.yml checked out the framework with no ref at all -- floating main. So verify has been checking a fixed, published tree against a *moving* emitter while publish builds from a pinned one. They agree today only because chock main happens to be two CI-only commits past v0.7.0. The next merge that changes emitter output turns this check red on a tree nobody touched, and the failure would read as tampering in the one repository whose entire guarantee is "this content is what the catalog published". Both workflows now derive the ref from chock-catalog/.framework-ref: - generated-only checks out the catalog first, reads .framework-ref into a step output, and checks out the framework at that ref. Verify now reproduces exactly what a publish from that catalog ref would produce. - publish keeps the dispatch input -- a human overriding it is the reason it exists -- but its default is the sentinel `auto`, resolved in a step from the catalog ref being published. A sentinel rather than an empty string so the resolved value is visible in the run log and a blank input resolves the same way. The commit message the publish writes now records the resolved ref rather than the literal input. The comment that failed is replaced rather than restated. It said "Bump this default with each framework release" and was skipped three times; prose does not execute. The new one says what the value means -- the emitter version whose output *is* this repository's content -- and the mechanism now makes the correct value the default. The dry run was hiding every added file. `git --no-pager diff --stat` reports tracked files only, so a publish's additions are invisible and a human reading it to decide whether to publish is shown only what disappears. On the v0.7.0 publish that printed "15 files changed, 14 insertions(+), 2450 deletions(-)" for a rename into a larger file; the real change was 5677 insertions. `git add -A -N .` first, which records paths without staging content, so the worktree and the later commit are unchanged. The Publish step's own gate had the same defect with a worse consequence: on a build whose only change is added files, `git diff --quiet` is clean, so it printed "No change to publish." and exited 0 having published nothing. Reproduced locally -- exit 0 with an untracked plugin directory present, exit 1 once intent-to-add is recorded. Staged there too. Alongside, three hand-maintained files that are hygiene on their own merits and happen to be what a public plugin catalogue asks for: - SECURITY.md, which routes a report to where it can be fixed. This repository is compiled output, so a fix landed here is overwritten by the next publish: emitter and packaging defects go to chock, policy content to chock-catalog, and only distribution-integrity findings are genuinely this repository's. No SLA and no PGP key are invented; it points at chock's advisory route and its timelines. - assets/icon.svg, the org mark from the brand family, unchanged except for explicit width and height so "512x512" is unambiguous to a consumer that does not read viewBox. - .github/workflows/hol-plugin-scanner.yml, running the HOL AI plugin scanner on push and pull_request as a report, not a gate. The score thresholds stay at the action's non-failing defaults deliberately: the scanner flags two of these plugins HIGH for quoting the patterns they block, and a gate that fails on a guardrail describing a guardrail only teaches people to route around it. submission_enabled is pinned false explicitly rather than left to the default, because that input can open issues in third-party catalogue repositories. The action is pinned by commit, not by the `v1` tag its docs give: `v1` has been re-pointed across 560 releases, so `@v1` lets the code running here change without a diff in this repository. None of this touches generated content. Verified per repository by mirroring the generated-only job locally -- dist, catalog and framework as siblings, pip install ./framework, the repository's own build lines -- and confirming `git diff --exit-code` and `git status --porcelain` are both silent, before and after. actionlint and zizmor --offline --min-severity low are clean. Signed-off-by: Claude <noreply@anthropic.com>
|
CI has run. Supersedes the "GitHub Actions itself", "Python version" and " All checks green on the first run: The derivation on a real runner, from the The step read Two caveats stand, and I am not quietly dropping them:
Generated by Claude Code |
Owner decision 2026-09-01: the scanner's unique value is a semantic check on shipped plugin content independent of the reproduction check, which only matters once the catalog takes external contributions or has real adopters. Today it would run never-exercised third-party code and carry two permanent HIGH false positives (policies quoting the patterns they block) — standing noise that teaches readers to ignore the report. SECURITY.md, the icon, and the framework-ref derivation stay. Signed-off-by: Claude <noreply@anthropic.com>
Lead with the surprises
1. The dry-run bug is worse than "the stat misleads a reader" — it made the publish itself a silent no-op.
git diffreports tracked files only, so on a publish whose only change is added files the review step printed nothing and thePublishstep's owngit diff --quietgate was clean, printedNo change to publish., exited 0, and pushed nothing. Reproduced below with the workflow's own step bodies:origin/maindoes not move.2.
verifyandpublishwere never checking the same thing.generated-only.ymlchecked out the framework with noref:at all — floatingmain— whilepublish.ymlbuilt from a pinned tag. They agree today only becausechockmainis two CI-only commits pastv0.7.0; I confirmed this repository's published tree reproduces clean under bothmain(4cd85b3) andv0.7.0(a1fb5bb), which is the only reason this has not already gone red.3. My first old-vs-new test rig was contaminated and I had to throw it out. Steps on a runner share one working tree, so the intent-to-add records written by the review step are still in the index when the publish step runs. A rig that reuses one copy silently hands the old publish body the new body's staging, and the first run showed the old body committing correctly. The results below run each variant from a pristine copy with its own bare
origin.What changed
Three pins name the framework and none were related: the catalog's
.framework-ref(v0.7.0),publish.yml'sframework_refdefault (v0.4.0for three releases before being corrected), andgenerated-only.yml's framework checkout (no ref at all). Both workflows now derive fromchock-catalog/.framework-ref:generated-only.ymlchecks out the catalog first, reads.framework-refinto a step output, then checks out the framework at that ref. Verify now reproduces exactly what a publish from that catalog ref would produce.publish.ymlkeeps the dispatch input — a human override is why it exists — but defaults to the sentinelauto, resolved in a step from the catalog ref being published. A sentinel rather than an empty string so the resolved value is visible in the run log and a blank input resolves the same way. The commit message a publish writes now records the resolved ref, not the literal input.git add -A -N .before the stat, and in thePublishgate for the same reason.SECURITY.md,assets/icon.svg,.github/workflows/hol-plugin-scanner.yml.The change is the same in all four distribution repositories; the four
run:bodies touched here are byte-identical across them (compared by sha256 of the extracted script, not by eye).Evidence
Generated content is untouched — before and after
Mirrors the
generated-onlyjob:dist,catalogandframeworkas siblings,pip install ./framework, this repo's own build lines, then bothgit diff --exit-codeandgit status --porcelain(the latter becausegit diffalone will not show added files — the very bug in Task 2).Before any change, framework at
main(4cd85b3):After the change, framework at
v0.7.0— the ref the new workflow derives:So
SECURITY.md,assets/icon.svgand the new workflow do not breakgenerated-only.The derivation actually derives — run against this repository's committed workflows
The resolution step's script is extracted from the committed YAML (not retyped) and executed with
bash --noprofile --norc -e -o pipefail, as the runner does, against a catalog directory whose.framework-refis varied. The value read back is what the step wrote to$GITHUB_OUTPUT:Changing
.framework-refchanges what gets checked out; a missing or empty file fails loudly instead of silently defaulting; an explicit ref still wins.The dry run, old vs new, each on a pristine copy
Run in
chock-codex-plugins— the step bodies are byte-identical to this repository's, so the behaviour is the same, but the run itself was there and I am not going to claim otherwise. Both bodies extracted from the committed YAML;originis a local bare repo so the push path runs for real.The no-op path is unchanged. The additions-only path goes from "reports nothing and publishes nothing" to "reports 36 insertions and publishes them".
Linters
actionlintv1.7.7 (the versionsecurity.ymlinstalls) andzizmor --offline --min-severity low .github/workflows/, run over all five workflows in this repository:Both were clean before the change too, so this neither introduces nor hides a finding. No
persist-credentials: falsewas weakened; thedistcheckout inpublish.ymlremains the documentedartipackedexception.Notes on the Task 3 files
SECURITY.mdroutes a report to where it can be fixed rather than inventing a surface this repository does not have: emitter and packaging defects tochock, policy content tochock-catalog, and only distribution-integrity findings genuinely here — a fix landed on a generated file is overwritten by the next publish. No SLA and no PGP key are invented; it points atchock's advisory route and its timelines.assets/icon.svgis the existing org mark fromorg-plan'sbrand/, byte-identical except for explicitwidth="512" height="512"alongside the existingviewBox, so "512×512" is unambiguous to a consumer that does not readviewBox. No new mark was invented.hol-plugin-scanner.ymlruns the scanner on push and pull_request as a report, not a gate, and the workflow comment says why: two of these plugins are flagged HIGH for quoting the very patterns they block, so a gate armed on that would train people to route around it.submission_enabledis pinnedfalseexplicitly rather than left to the default, because that input can open submission issues in third-party catalogue repositories and that is an owner decision. The action is pinned by commitf344c80c093464413432b399f38a7ba2f3365ae2, resolved fromv1and equal to tagv1.2.560;v1has been re-pointed across 560 releases, so@v1would let the code running here change without a diff in this repository.What I could not run
claude plugin validatesteps in this repository's workflows were not exercised; the change does not touch them.zizmorversion. CI runszizmor-actionv0.6.2; locally the CLI is v1.30.0.action.ymlat the pinned commit, nothing more.Generated by Claude Code