Repository navigation
fix(release): the release-integrity backfill builds from the version commit's tree, not github.sha - #21008
Conversation
…commit's tree, not github.sha The audit already selects the version commit and makes a worktree of it; it now hands that tree to the backfill steps (version-tree output), which install, create the Releases (target_commitish and CHANGELOG permalink at the version commit) and regenerate and attach the ADR-0087 D4 asset there, each refusing a tree that is not at the version commit. The checkout and everything the audit reads are unchanged. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…he backfill and the publish D4 step from release.yml A landing that moved packages/spec/src after the version commit is the fixture; the attached spec-changes.json must equal the publish job's manifest byte for byte, every Release is created at the version commit, and each backfill step refuses a tree that is not at it. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…ckfill-version-commit
Contract reviewServed-tier: Inputs: card #20982 (body; comments 5922583890 triage, 5922727017 claim, 5923103867 dev report, 5923147764 ruling), PR #20625 and PR #20978 bodies, PR #21008 (body, file list, net diff against ① Derived judgmentsAccept-set and public-surface changes implied by the diff: none. The two files are The audit step (release.yml:834-1053): nothing it reads moved. The only change inside the step is one output line after the pre-existing The three backfill steps (:1083-1146): the guard fails closed. Each carries Install (:1083-1094). Releases backfill (:1096-1121). D4 backfill (:1123-1146). Running the version commit's own copies of the scripts under the current workflow: intended and safe. Intended: ADR-0125's amendment (:168) states that the publish job's steps are the workflow at Lane header (:743-745). "Reads github.sha and the version commit it selects, both out of the object database" is right. "every backfill builds from that version commit's tree, as the publish job does" is over-broad by one word: this job also requests the runtime-image backfill ( Block comment (:1058-1078) and the two step comments. "The publish job checks out the version commit and runs that commit's own scripts" right (:1309; ADR-0125 amendment D1 double-prime). "The checkout itself is NOT swapped" right (above). "an empty version-tree would leave the step in this checkout, which is the defect, with nothing said" right: PR body. Sentences tested against the tree, all right unless named: "Since PR #20625, the publish job builds the version commit" (:1309); "It already makes a worktree of the version commit" (on The pin, battery 13: it executes the workflow's step text, not a paraphrase. ② Semver levelNo changeset, ③ Boundary flagsDev report 5923103867: Check-runs on Implemented-by: VERDICT: PASS Adopted and posted by
Generated by Claude Code |
Closes #20982
Clause-②: no (release wiring; no published package's accept set or public surface moves, per the question in
scripts/pm/clause2-line.mjs)What this changes
release.yml'srelease-integrityjob repairs an already-published release by backfilling its GitHub Releases and its ADR-0087 D4 asset (spec-changes.json). Before this PR, those backfill steps ran in the job's checkout. That checkout isgithub.sha, the head of whichever push is being audited. Since PR #20625, the publish job builds the version commit instead. As a result:packages/spec/srcmade a repaired D4 asset describe a tree npm did not ship.target_commitishand the CHANGELOG permalink at that landing.After this PR, every backfill step builds from the version commit's tree, as the publish job does. Per triage
5922583890and the claim5922727017:git worktree add --detach, from the object database, for the whole-group probe). It now passes that worktree to the backfill steps as a new step output,version-tree. Nothing else in the audit moves. The checkout is not swapped, the audit still readsgithub.sha, and its tripwire still reads the workspace.Install dependencies (the version commit's tree).pnpm install --frozen-lockfileruns in that worktree, against the version commit's lockfile. Thegithub.shacheckout is no longer installed, because nothing reads it after the audit.scripts/release-github-releases.mjsruns in its own tree, so every package and everyCHANGELOG.mdis read from there.GITHUB_SHAis set to the version commit for that one process. The script uses that value astarget_commitishand as the ref of the CHANGELOG permalink. GitHub ignorestarget_commitishwhen the tag already exists. But a publish that died before its tag push leaves no tag, and the Release then creates the tag attarget_commitish.release-spec-changes.sh --prepareand--attachrun in the worktree, with that commit's generator, exactly as the publish job's own D4 step does. So the asset a repair attaches is the manifest the publish built.git -C "$VERSION_TREE" rev-parse HEADwithversion-commit. Without this check, an emptyversion-treewould silently leave the step in the checkout, which is the defect.github.shaand the version commit it selects, and that every backfill builds from that commit's tree.scripts/release-spec-changes.shis unchanged. It needs no tree argument, because the version commit's own copy runs in the version commit's tree.Pin
The pin is battery 13 of
node scripts/release-verify-npm.mjs --self-test, whichlint.ymlalready runs as "Post-publish npm verification self-test". Battery 12, next to it, pins when the audit backfills; battery 13 pins which tree the backfill builds from. The self-test now has 93 cases across 13 batteries (82 across 12 before), and the roster floor is 13.packages/spec/src. The release scripts are committed into the fixture together with their imports. A symlink would not work: Node would resolverelease-github-releases.mjsto this repository and read this repository's workspace.release.yml. Each step'senv:is read from the YAML too, and every expression in it is either resolved or refused. The five steps are:github.shaset to the landing;ref:checks out).npm:view,versions, and a realpack.gh: records each upload.pnpm: records each install. It also stands in for the generator: it finds the workspace above its cwd, as pnpm does, and writes a manifest that is a pure function of that workspace'spackages/spec/src.version-treethat is at the version commit.target_commitishequal to the version commit.CHANGELOG.mdat the version commit.spec-changes.jsonis byte-identical to the publish job's manifest.version-tree, with nothing installed, created or attached.version-treeat the landing, with nothing installed, created or attached.release.ymlitself.Ablations
Each leg starts from the committed fix and goes through
node scripts/ablation-replace.mjs. The anchor had to hit, and the blob changed on disk. The tool wrapped the self-test, under a trap. Every restore was proven by the file's blob matching HEAD's and by an emptygit diff HEAD.release.ymlcd "$VERSION_TREE"before--prepare, so it runs in the checkout againGITHUB_SHA="$VERSION_COMMIT"override--expect 3)version-treeoutput lineThe first attempt at the first leg was a no-op: the replacement text was already in the file.
ablation-replacerefused it before anything ran, and the leg was re-run with a distinct marker.Gates
node scripts/release-verify-npm.mjs --self-testexits 0 on68e56d7e65(93 cases, 13 batteries).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackover this diff (2 paths) derives 50 commands. All 50 exit 0 on68e56d7e65;dispatch-gates --ran: 50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN;check:pm-dispatch-gates1976 cases pass (dev report5923103867).scripts/file publish nothing, soskip-changesetapplies.Acceptance notes
Create GitHub Releasesstep. It runsrelease-github-releases.mjswith Actions'GITHUB_SHA, so its truncated bodies linkCHANGELOG.mdat the head of the queuing push. On a repair dispatch, that head can be well past the version commit. Its CHANGELOGs are still read from the version commit's checkout. Its tags already exist by then, becauserelease-publish.shpushes them beforepublished=true, sotarget_commitishis unused. The linked file at the later head still carries the entry. This is outside this card's surface (the backfill). Carrier: none.github.sha'spackageManagerpin, bysetup-pnpm. Corepack then runs whatever the version commit'spackage.jsonpins. If the two differ, the version commit's pnpm is downloaded once. That is the same pnpm the publish job uses, and the Corepack cache is not involved, because it is saved insidesetup-pnpmbefore this step runs.Generated by Claude Code