Repository navigation
spec(changes): delete the committed spec-changes.json and its merge=os-regen route; both projections are gitignored and generated at publish only (#22449 B′, card ③) - #22638
Conversation
…the judged rev, never read from a committed spec-changes.json (#22485) check-adr-0087-registration read packages/spec/spec-changes.json at HEAD as its witness that extractIds() still sees every migration id the build sees. Since the committed copy stopped being regenerated it ages, and a migration id withdrawn from the registry would have stayed in it and false-redded the gate; deleting the copy would red it outright. The witness is now generated at the rev being judged: git archive of that rev's generator inputs, the package's own build-spec-changes.ts --out under the checkout's tsx. Every failure to produce it is a red input, never a skip. Self-test: battery W1-W6 drives the real generatedWitness over a fixture generator (withdrawn id green, head-not-worktree, missing/failing/silent generator, no tsx); I1 and the entry-point run go through the real path too. objectui-changeset-digest's fw-gate sandbox stages the same stand-in generator. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
…s-regen route; gitignore both projections; an unrouted pending path prints a remedy that works (#22485) Ruling 6078203801 on #22449 (B'): the committed copies and their two merge=os-regen routes are deleted. The guide's half landed with #22483; this is the spec-changes.json half. - packages/spec/spec-changes.json leaves git and is gitignored, with packages/spec/protocol-upgrade-guide.md. The publish lane still writes both into the package before npm pack, and files[] still ships them. - .gitattributes drops the last projection route; regen-artifacts.mjs moves gen:spec-changes to NOT_DRIVER_MANAGED as untracked output, which the merge-driver self-test now holds against git. Comment counts in git-merge-regen.mjs follow (16 routed paths, 16 rows, 14 defaulted). - build-spec-changes.ts writes no committed copy: its no-flag mode writes the gitignored package copy the lane and gen:spec-changes produce (projection-cli's committedPath becomes defaultPath). - check-regen-pending: a pending path with no REGEN_ARTIFACTS row stays blocked, but now says why and prints the remedy that settles it, and --release PATH removes exactly that line (refusing a routed path or one that is not pending). Its fixture derives its pending row instead of naming spec-changes.json. - check-generated's row text, os-regen-merge.sh's measured-path note and check-future-spec-major's exemption row for the deleted copy (forced: it matched nothing once the file was gone) follow. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
…d the refusal printed (#22485) Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
… case, not a crash (#22485) Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
…lete-committed-projections
…enerate and commit the two projections (#22485, rider from pointer 6094908270) An entry regenerates registry.ts alone (gen:migration-registry); the two ADR-0087 D4 projections are generated at publish and in memory on the pull request, and neither has a committed copy any more. The #8344 merge-queue measurement stays, marked as history kept for its registry.ts conclusion. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
维护者速读
|
…as main builds them ADR-0087's D4 progress note records #22483 (PR #22556) and #22485 (PR #22638) as landed and its other not-yet-landed sentences move to the past tense; the ruling quotation is untouched. AGENTS.md re-counts the checked-in generated artifacts (six) and the ADR-0087 row of the regenerate table says there is nothing to commit. The retirement playbook sends step edits to registry.ts.template and drops the gen:spec-changes / gen:upgrade-guide laps. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011u73oxZ5X95qrARPTeoU6v
Fixes #22485
Clause-②: no
The committed
packages/spec/spec-changes.jsonand itsmerge=os-regenroute are deleted, and both ADR-0087 D4 projections are gitignored inside@objectstack/spec. This finishes ruling6078203801on #22449 (B′): "The committed copies and their twomerge=os-regenroutes are deleted;check:spec-changesandcheck:upgrade-guidestop comparing a committed copy." The checks stopped comparing in #22533 (#22482). The guide's copy and route went in #22556 (#22483). This PR removes what was left: thespec-changes.jsoncopy, its route, and every reader of it.What ships does not change. The publish lane still writes both projections into the package right before the tarball is packed,
files[]still lists them, and--verifystill refuses a tarball that is missing one. Proof is in the dry run below.What changed
packages/spec/spec-changes.jsonis deleted from git..gitignorenow lists it andpackages/spec/protocol-upgrade-guide.md, which is spec(changes): generate the per-major spec-changes section and the protocol upgrade guide at publish; the pull request still generates both in memory and renders the diff (#22449 B′, condition 1) #22482's note..gitattributesno longer routes either projection.scripts/regen-artifacts.mjsdrops the row and recordsgen:spec-changesinNOT_DRIVER_MANAGEDas untracked output. The merge-driver self-test holds that entry against git.check:merge-drivernow reads 16 routed paths and 5 untracked dispositions. The comment counts inscripts/git-merge-regen.mjsnow read 16 paths and 16 rows, 14 of them defaulted.build-spec-changes.tswith no flag writespackages/spec/spec-changes.json, which is now untracked and gitignored. It is the same filerelease-spec-changes.sh --prepare/--generatewrite forfiles[]to ship, and the onegen:spec-changeswrites locally.--checkand--outare unchanged. Inlib/projection-cli.ts,committedPathbecomesdefaultPath.build()is byte-for-byte untouched, so the generated bytes are unchanged.release-spec-changes.shcalls it at--generate, at the first-ever--prepare, and at--prepare --previous-package. Cloud's changed-spec-surface guard runspnpm run gen:spec-changesin its pinned framework checkout and reads only that output (cloud#2750, comment6083243420on spec(changes): generate the per-major spec-changes section and the protocol upgrade guide at publish; the pull request still generates both in memory and renders the diff (#22449 B′, condition 1) #22482). A usage error there would turn cloud's only required check red on its next pin move, and would also need an edit to the release script, which is outside this card's file surface. The guide's no-flag mode moved to a different target. This one keeps its target, and the target is no longer tracked.check-generated.ts's row now names the gitignored copy.os-regen-merge.shgains a note that its 2026-09-17 readings were taken over a path that has since left the route list. That script reads the routed list from.gitattributesat run time and hard-codes none.6094908270, carried from the spec:ListView.userActions.editInlinedefaults to true in v18 andeditInline: falseis the opt-out; objectui reads an absent key as on (maintainer ruling 2026-10-10, after objectui#5144) #22605 dev):packages/spec/src/migrations/entries/README.md, under "What this does not fix", told authors that both projections "still have to be regenerated and committed when an entry lands". It now says what is true after the deletion. An entry regeneratesregistry.tsalone, withgen:migration-registry(gate:check:migration-registry). The two projections are generated at publish, and in memory on the pull request. ⛔ Neither is regenerated for an entry. Thespec-changes.jsonandprotocol-upgrade-guide.mdare unsharded generated artifacts whose merge safety rests on a LOCAL-only git driver — the merge queue rebuilds server-side, where no custom driver runs #8344 merge-queue measurement stays, marked as history kept for itsregistry.tsconclusion.scripts/check-future-spec-major.mjsdeletes itsQUOTATION_EXEMPTIONSrow for the deleted copy. Measured: HEAD's version of the gate, run on this tree, exits 1 withQUOTATION_EXEMPTIONS entry 5 (packages/spec/spec-changes.json, major 4997) matched NOTHING. After the deletion the gate exits 0 with6 witnessed ledger entr(y|ies), every witness still matching.scripts/objectui-changeset-digest.mjs: itsfw-gatesandbox runs a copy of the real ADR-0087 gate, so it now stages the stand-in witness generator instead of a fixturespec-changes.json. Without that change, its round-trip cases die at the input assertions.H1: every reader of the committed copy, and what it reads now
scripts/check-adr-0087-registration.mjs(assertInputs, formerly:2184)packages/spec/scripts/build-spec-changes.tsno-flag modepackages/spec/scripts/check-generated.tscheck:spec-changesin memory. That check never goes stale, so--fixnever regenerates. The row text names the gitignored copy.scripts/regen-artifacts.mjs,.gitattributes,scripts/git-merge-regen.mjs,.githooks/pre-commit→check-regen-pending.mjsscripts/pm/os-regen-merge.shscripts/release-spec-changes.sh(--prepare/--generate/--verify/--attach)packages/spec/package.jsonfiles[]scripts/check-release-spec-changes.mjsscripts/release-verify-npm.mjs(MANIFESTat:2063)packages/spec/scripts/render-projection-diff.ts(legacyPath)--out. The legacy path is read only inside a throwaway archive of a base that predates--out, never from this checkout. Unchanged.packages/cli/src/utils/spec-release-changes.tsreleasesection. The committed copy never had one, so it answerednullin the workspace before this PR, and an absent file answersnulltoo. Unchanged.packages/spec/src/**/*-retirement.test.ts(EXCLUDED_PREFIXES)gen:spec-changesoutput sits there, gitignored. Unchanged.scripts/check-future-spec-major.mjsscripts/check-regen-pending.mjsself-test fixturescripts/objectui-changeset-digest.mjsself-test sandboxscripts/audit-spec-changes.mjsgen:spec-changesoutput in its pinned checkout since cloud#2750. That output path is unchanged here.H2: the witness is generated at the judged rev
Before this PR,
assertInputsreadHEAD:packages/spec/spec-changes.jsonas its independent witness thatextractIds()still sees every id the build sees. Since #22533 that copy has been aging. A withdrawn migration id stays in an aged copy and false-reds the gate, and deleting the copy reds it outright.generatedWitness()now lays out the judged rev'spackages/spec/{src,scripts,package.json}withgit archive. It borrows the checkout'snode_modulesand runsbuild-spec-changes.ts --outunder the installedtsx. So the witness describes exactly the rev being judged, never the working tree. A missing runner, a missing generator, a generator that exits non-zero, or one that writes nothing is each a red input, never a skip. On this tree the real run generates 414 ids, all of them seen by the parser. The gate run is about 2 s.W1-W6, with 9 cases, drives the realgeneratedWitnessover a stand-in generator. The stand-in evaluates the fixture registry under tsx, as the real one does.scripts/ablation-replace.mjsat538bec7e1d. The witness read was put back to the committed-copy read.W1: a withdrawn migration id must NOT red the gate -- got: ledger parser drift … - withdrawn-one.7e383d48ea90== HEAD, andgit diff HEADis empty.H3: a pending path whose route was retired
The premise holds for the class, but not for this card's own path. The deletion does not reach the stuck marker: git runs no merge driver on a modify/delete, so a branch carrying
spec-changes.jsonwrites no marker for it (H4). The stuck marker comes from the guide's route, retired by #22483, and from any marker an earlier merge left.Measured with the real driver and hooks, in a detached scratch worktree:
874a38d8d9, which is514bf3c101^1, before docs(spec): the protocol upgrade guide gets a public address, and docs/protocol-upgrade-guide.md stays as a committed pointer stub so the published pointers keep resolving (#22449 B′, condition 2) #22483. A commit there editsdocs/protocol-upgrade-guide.md. It then merges this branch (cec190b908).docs/protocol-upgrade-guide.md, and the tree holds the branch's generated guide: the stub was dropped.git diff HEAD^2shows +1590 −9.git checkout HEAD^2 -- docs/protocol-upgrade-guide.mdrestores the 15-line stub.--releaseexits 0 with "nothing left pending, marker cleared".--pre-pushexits 0.18d999031b'scheck-regen-pending.mjs, run on the same marker, exits 1 withrecorded as pending but absent from scripts/regen-artifacts.mjs (cannot verify)andThis check clears itself the moment they are current — nothing to reset by hand.. It has no--release, and its run leaves the marker in place.The path stays BLOCKED, never passed. Passing it would land exactly the drop the driver made, and #22556 recorded the refusal as the one thing that keeps that drop loud.
--release PATHrefuses a path that still has a row, because its gate discharges it, never a hand. It also refuses a path the marker does not hold, and keeps every other line and the deferral record.fixtureSelfTest, in the script's own self-test harness):--releaserefuses a routed path and an absent path, and leaves a routed path's debt and itsstalerefusal intact.REGEN_ARTIFACTS, so retiring a row cannot break the fixture again.scripts/ablation-replace.mjsat538bec7e1d. The printed--releaseline was dropped.ca127a866a97== HEAD, andgit diff HEADis empty.H4: a branch that still carries a regenerated copy
What the author does:
git rm packages/spec/spec-changes.json && git commit --no-editThe conflict is modify/delete, so git runs no merge driver and leaves no marker. The deletion is the resolution, and nothing regenerates the copy in git any more. Measured on a scratch commit at
18d999031bthat edited the copy, mergingcec190b908:CONFLICT (modify/delete): packages/spec/spec-changes.json deleted in cec190b908… and modified in HEAD, and no marker is written;PR #22421 (#11509, seat 2) is such a branch today. The same line is in
.gitattributesand inNOT_DRIVER_MANAGED's reason, which is where a merging author meets the route list.Publish lane dry run (no release act)
At
928bee8573, withRELEASE_VERSION=17.7.0:--generateexits 0 and writes both files intopackages/spec/.git status --shortstays empty:--ignoredshows both as!!.--verifyexits 0 with✓ registry projections verified against a fresh generation: spec-changes.json (2 per-major record(s), 121 converted / 414 migrated) and protocol-upgrade-guide.md (1617979 bytes) match the artifact.and✓ no per-release section is owed: this lane (--generate) publishes none..objectstack-spec-17.7.0.tgzlistspackage/spec-changes.jsonandpackage/protocol-upgrade-guide.md. Itsspec-changes.jsonis byte-identical to the generated one (cmp).spec-changes.jsonremoved,--verifyexits 1 withthe artifact about to publish ships no spec-changes.json. A consumer'snode_modulescannot be handed a tarball without it.Changeset: none,
skip-changesetNo published byte changes:
build()is untouched, so the lane generates the same bytes;packages/spec/scripts/**is not infiles[];So no package warrants a bump, and this PR takes
skip-changeset. The dispatch asked for H4's line to go in a changeset. With no changeset, that line is in H4 above and in the two in-repo places named there.Governed lines that name the copies or their routes (not edited here; the skills lane carries them)
docs/adr/0087-metadata-protocol-upgrade-contract.md:263–:266: "the deletion of the committed copies (the per-major projection inpackages/spec/spec-changes.json, the guide body atdocs/protocol-upgrade-guide.md) and of their twomerge=os-regenroutes in.gitattributes— spec(changes): delete the committed spec-changes per-major projection and the upgrade guide copy, with their two merge=os-regen routes, once generation at publish has landed (#22449 B′) #22485, which lands last." It is written as not yet landed.docs/adr/0087-metadata-protocol-upgrade-contract.md:268–:270: "Until spec(changes): delete the committed spec-changes per-major projection and the upgrade guide copy, with their two merge=os-regen routes, once generation at publish has landed (#22449 B′) #22485 lands the committed copies stay in the tree, and the generators' no-flag mode (gen:spec-changes,gen:upgrade-guide) still writes them, butcheck:spec-changesandcheck:upgrade-guideno longer compare them". The guide half was already stale since docs(spec): the protocol upgrade guide's public address is one docs-site page per protocol major; docs/protocol-upgrade-guide.md becomes a pointer stub (#22449 B′, condition 2) #22556.docs/adr/0087-metadata-protocol-upgrade-contract.md:283–:284: "The 2026-08-06 note above ("deliberately unsharded") is about that committed copy, which spec(changes): delete the committed spec-changes per-major projection and the upgrade guide copy, with their two merge=os-regen routes, once generation at publish has landed (#22449 B′) #22485 removes."docs/adr/0087-metadata-protocol-upgrade-contract.md:207–:208: "spec-changes.jsonis deliberately unsharded — it is keyed by version, which has never been a conflict surface." This is about the committed copy, which no longer exists.docs/adr/0087-metadata-protocol-upgrade-contract.md:438–:440: "Those copies stay in the tree until spec(changes): delete the committed spec-changes per-major projection and the upgrade guide copy, with their two merge=os-regen routes, once generation at publish has landed (#22449 B′) #22485 deletes them, andcheck:spec-changes/check:upgrade-guideno longer compare them."docs/adr/0087-metadata-protocol-upgrade-contract.md:219–:220: the ruling's own words, quoted. They are listed only because they name the routes, and they are not to be edited.AGENTS.md:717: "| An ADR-0087 conversion / migration registry |check:spec-changes,check:upgrade-guide|gen:spec-changes,gen:upgrade-guide|". Neither check can go stale any more, and both generators write only untracked files. The row that holds for an entry ischeck:migration-registry/gen:migration-registry.AGENTS.md:706: "packages/spechas eight checked-in generated artifacts, each with its own CI gate." The two projections are no longer checked in, so the count needs a re-measure..claude/skills/spec-property-retirement/SKILL.md:260: "然后gen:spec-changes、gen:upgrade-guide、gen:api-surface、gen:docs。".claude/skills/spec-property-retirement/SKILL.md:336–:337: "所以纯散文改动也要gen:spec-changes+gen:upgrade-guide,和任何别的改动一样。"docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md:459–:460: "regenerate with the conversion entry (gen:spec-changes,gen:upgrade-guide)." This is a record of what that PR did, likely left as history.Measured and not stale:
.claude/skills/pm-dispatch/SKILL.md:296, the hot-file row foros-regen-merge.sh, which names no path, andreferences/landing-operations.md:38, which reads.gitattributeslive.Verification
Gate union read at
cb1ac74e76, the final head. It is the 87 commands the dispatch named, plus whatnode scripts/pm/dispatch-gates.mjs --commandsderives on this diff (105 today), 110 commands in all. Each ran with its exit code captured before any pipe, and all 110 exited 0.--ranreportsRun reconciliation — 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN.That includes:check:merge-driver: 16 routed paths, and5 untracked disposition(s) still hold — git tracks none of those paths;check:changeset-gate-self-testsand the ADR-0087 gate itself (--base origin/main: green, with a generated witness);check:generated:✓ All 15 generated artifacts are up to date, withcheck:spec-changes spec-changes.json (gitignored; written into the package at publish);check:objectui-changeset,check:pm-dispatch-gates,check:type-check-debtandcheck:nul-bytes.Under
scripts/pm/os-verify-lock.sh(VERDICT command-exit 0, at928bee8573; the README rider after it touched no test input):@objectstack/spec:buildexit 0;typecheckexit 0, andtsc -p tsconfig.scripts.json --listFileslists all four touched script and test files;vitest --project local: 642 files, 19146 passed, 1 todo;--project repo: 54 files, 915 passed;release-verify-npm.mjs --self-test: 115 cases across 13 batteries;check-release-spec-changes.mjs --self-test: 31 batteries.Acceptance notes (not filed)
6091886885, corrected by6091996837) ends when this lands..github/workflows/lint.yml:4728listsspec-changes.jsonamong the artifactsmerge=os-regenprotects. Comment only. carrier: none..github/workflows/pr-automation.yml:959–:961: "the two scripts import only node builtins and repo-local modules, so a failed install cannot change their verdict". The import half is still true. The ADR-0087 gate now needs the installedtsxfor its witness, so a failed install also reds that step, in the enforcing direction. Comment only. carrier: none.scripts/check-published-files.mjs:291: thespec-changes.jsonregistration reason could say "generated at publish (it is not in the tree)", as the guide's does. carrier: none.docs/spec-generated-artifact-sharding.md:42describes the committed file's merge shape as current. Design history. carrier: none.check-adr-0087-registration --self-testcosts about 7 s more on this shared box (20.7 s → 27.5 s): 8 real generator spawns.Generated by Claude Code