Repository navigation
fix(release): re-date a pre-written current-series field on its GA's own version run - #25407
Merged
Merged
Conversation
…own version run sync-release-index-currency left a same-version status field alone on every run, date included. That is right for prerelease cuts (the 17.7.0 re-dating its header records), but on the run whose `changeset version` writes a GA's CHANGELOG heading the run's date is the true one, and a field written by hand ahead of the cut is not: 18.0.0 shipped dated 2026-10-19 while its version pass ran and npm published on 2026-10-20. The run is detected without a clock or the network, per major: the newest GA's heading is in the working tree's spec CHANGELOG and not in the one committed at HEAD (read with git show, GIT_* stripped). Only then is a same-version field re-dated. Prerelease runs, runs with nothing to version and runs after the version commit landed keep the refusal. An unreadable HEAD is a refusal, and rewriteIndexText now requires the HEAD CHANGELOG instead of defaulting either way. Self-test: new battery Control I (16 cases, floor 9 batteries); Control H's end-to-end tree is now a real throwaway repository. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SFSGWz48S21trBAfXEXUx
This was referenced Oct 23, 2026
This was referenced Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Elon · project thread
Fixes #25356
Clause-②: no
Metadata-protocol: none
A code PR on one file,
scripts/sync-release-index-currency.mjs(+322 / -47). It touches nocontent/docs/releases/**file, no workflow and no package.What changed
The rewriter refused to touch a
current series:field that already names the newest GA, date included, on every run. That refusal is right for prerelease cuts: its header records the 17.7.0 field that every18.0.0-next.Ncut re-dated. It was wrong on one run: the run whosechangeset versionwrites the GA's CHANGELOG heading. On that run the run's date is the true one, and a field written by hand ahead of the cut is not. 18.0.0 shipped dated 2026-10-19 that way.Now:
committedChangelogText(root)readspackages/spec/CHANGELOG.mdas committed atHEADwithgit show(GIT_* stripped bygitFreeEnv(), perGIT_ENV_ISOLATION_RULE). It uses no clock and no network. AHEADgit cannot read is a refusal, because either default would bring back one of the two defects.gaVersionRunMajors()returns the majors whose newest GA heading is in the working tree and not atHEAD. Those are the majors on their GA's own version run.rewriteStatusField(field, newest, date, { gaVersionRun })re-dates a same-version field only whengaVersionRunis true. A re-date that changes nothing returns null, so a current index stays unwritten. The refused shapes ("final release:", trailing matter, prose) stay refused on the GA run too.rewriteIndexTextnow requiresheadChangelogTextand refuses without it. A caller that forgot it would otherwise silently get the 18.0.0 behaviour back.syncIndexreads it withcommittedChangelogText, somain()and the self-test take the same path.main()labels a re-date in its output: "re-dated: this run's version pass wrote the newest v18 GA heading, so this is its version commit".Every other run keeps today's behaviour. A prerelease cut writes only a prerelease heading, which is never a GA. A run with nothing pending writes no heading. A run after the version commit has landed finds the heading at
HEAD.Two refinements of the card's guard, and why
The card words the guard as "the newest GA at HEAD differs from the newest GA after
changeset version". This PR keeps that idea with two changes:HEADcarries also "differs", and it is no version commit. The predicate is "this major's newest GA heading is absent atHEAD", which is exactly "this run'schangeset versionwrote it". Control I pins the lost-heading case.Where it runs, and the governed check
versionscript (pnpm run version). release.yml'sversion-prjob runs that twice: in "Render the post-version tree" (line 561) and as changesets/action'sversion-script(line 939). cut-rc.yml runs it on its snapshot (line 473). In all three,HEADis the commit being rendered and the tree is clean before the pass; release.yml already diffs the output againstHEAD(git diff --name-only HEAD). Theversion-prcheckout is shallow (depth 1), which still holdsHEAD's tree.node scripts/pm/check-governed-merges.mjs --branch claude/project-thread-1r2arfatfbd6690: "governed-surface predicate: 0 of 1 path(s) hit the register (7 surfaces govern objectstack). ✅ NOT governed", size 369 changed lines (≤ 3000). No workflow file changes, so the lane is untouched.Proof
Self-test (
pnpm check:release-index-currency-sync, exit 0): "OK self-test: 58 cases pass". Before this change it was 42. The new battery Control I (16 cases, floored; roster floor 8 → 9) pins:HEAD= CHANGELOG in the tree).rewriteIndexTextrefuses withoutheadChangelogText.syncIndexon a real throwaway git repository: re-dated on disk on the GA run; left alone after the version commit lands; refused when git cannot read the CHANGELOG atHEAD, with the index untouched.Control H's end-to-end tree is now a real repository too: a prerelease cut on it still writes nothing.
Rehearsal on the real corpus (a throwaway repository holding this branch's index, spec CHANGELOG and the four scripts). The v18 field was pre-written by hand to
(current series: 18.1.0, released 2026-10-21)and committed asHEAD. Then## 18.1.0was added to the CHANGELOG, aschangeset versionwould add it:v18: (current series: 18.1.0, released 2026-10-21) → (current series: 18.1.0, released 2026-10-23) — re-dated: …, exit 0origin/main's script, same treereleased 2026-10-21(the 18.0.0 defect){"wrote":false,"rewrites":[],"gaVersionRun":[]}, field keeps2026-10-23The live tree:
node scripts/sync-release-index-currency.mjsprints "every in-scope entry … (v16, v17, v18) already names the newest GA release of its major … nothing rewritten", exit 0, and the tree stays clean.The 18.0.0 date this would have stamped. The Version Packages PR #24524's commit
31abb7184cwas authored 2026-10-20T11:10Z, and npm published 18.0.0 at 2026-10-20T12:51Z (from #25354). So the version run would have stamped 2026-10-20, the true day.Gates
At
fbd6690(this branch merged withorigin/main830a63a),node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 33 commands for the one path. Each was run with its exit code captured before any pipe. On the first pass, six returned PREREQUISITE NOT MET (exit 3:yamlandtypescriptnot installed in the fresh worktree):check-ci-filter-parity,check-closing-keyword-parity(both modes),check-comment-mask-corpus,check:cli-command-idsandcheck:driver-memory-census. Afterpnpm install --frozen-lockfileall six exit 0.--ranreconciles: "✓ dispatch-gates --ran: 33 derived famil(ies) accounted for — 33 run, 0 NOT-MEASURED". The script-specific families among them includecheck:release-index-currency-sync,check-scripts-symbol-anchors(both modes),check-self-test-wired(both modes),check-self-test-workflow-commands(both modes),check:entry-guard,check:nul-bytesandcheck:pm-dispatch-gates.Run before the push:
check-commit-card-trailers --range origin/main..HEAD✓,check-regen-pending --pre-pushexit 0.No package source moves, so no package
testortypecheckis owed. No test file outside the script names it (git grep), so its self-test is its whole suite. CI ownstest:repo, Test Core, the type-check lanes andpnpm lint; this PR's check-runs answer them.skip-changeset. The file lives in the private root package ("private": true), andscripts/is in no published package'sfiles[]. The label rides the create.Acceptance notes
version-prlane that is the day of the last Version Packages refresh. It equals the npm publish day only when the PR merges the same UTC day as its last refresh, which held for 18.0.0. This PR does not change that; the header'sreleaseDatealready states it.## 18.1.0was added: a run on the pre-written(current series: 18.1.0, released 2026-10-21)produced(current series: 18.0.0, released 2026-10-23). That is 18.0.0 with a date that is not 18.0.0's. This is the pre-existing stale-field path, which this PR keeps unchanged as the card requires. It is reachable from a version pass on a tree that carries such a field: a cut-rc.yml prerelease cut, or a manual run.check-release-section-coveragealready reports such a field onmain(advisory without--strict). Dedupe words:sync-release-index-currency,ahead of the cut,rewritten back,current series.os-dev.mdforbids HTML comments in GitHub text. Same shape as PR docs(releases): date 18.0.0 by its npm publish day, and the v17 page stops saying a plain install resolves 17.7.0 #25354; reported as a deviation.claude/project-thread-1r2arf. The claim (comment6359770957) records it as the identity.Generated by Claude Code