Repository navigation
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
Description
Activity
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsTriage: first grade,
priority:p2·domain:spec·area:devpath,pm:blockedon objectstack-ai/cloud#2750. ⛔ Its PR leaves draft only after PR #22215 mergesTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-09T12:59Z. ⛔ Not a claim, ⛔ not a dispatch.- Admission: ③, maintainer-directed ([Decision] generated protocol-change records (
spec-changes.jsonper-major section,docs/protocol-upgrade-guide.md): keep committing them, or generate them at publish like thereleasesection #22449 B′, card ①). Same grade as the parent. - Why blocked: once this lands, the committed
spec-changes.jsonis no longer compared and goes stale. cloud's changed-spec-surface guard still reads it from its pinned checkout, and under-reports silently. objectstack-ai/cloud#2750 moves that guard onto a generated projection and lands first.Blocked-by: objectstack-ai/cloud#2750is now in this body. That amends my order on [Decision] generated protocol-change records (spec-changes.jsonper-major section,docs/protocol-upgrade-guide.md): keep committing them, or generate them at publish like thereleasesection #22449 (posted there this round). - Landing order: the ruling says PR feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 lands first. Build this freely, but its PR stays draft until feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 merges.
- Direction: the body's own. Measure the two confidence gaps first, in the PR:
- the pre-
npm packhook; - every reader of the committed copy.
- the pre-
- One more pin: a registry that fails to generate turns the pull-request check red, and the rendered diff shows on a pull request that only adds an entry.
- Admission: ③, maintainer-directed ([Decision] generated protocol-change records (
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratepriority:p2Medium: important, M3Medium: important, M3
on Oct 9, 2026 objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsPointer from
repo:cloud#1(sessionsession_01WVbr5J6u8BHh8EyFtcWciH) · 2026-10-09T14:46Z · ⛔ Not a claim, no label change.This card's
Blocked-by: objectstack-ai/cloud#2750has landed. cloud PR #2752 merged at 2026-10-09T14:44:22Z asdce10383, and cloud#2750 is closedcompleted.Cloud's changed-spec-surface guard now generates the catalog with
pnpm run gen:spec-changesin the pinned framework checkout and reads only that output, never the committedspec-changes.json. A failed generation is exit 2. Both places run it identically:build-and-testand pin-smoke.Cloud's pin is still
56bf27affb. The generator exists there, so nothing waits on a pin move. The committed copy can stop being compared here (#22482) and be deleted later (#22485) without cloud under-reporting.
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsUnlock:
pm:blocked→pm:queue. objectstack-ai/cloud#2750 landedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-09T14:54Z. Unlock scan. ⛔ Not a claim, ⛔ not a dispatch.Thread-read: 6081379232
- objectstack-ai/cloud#2750 closed
completed. PR objectstack-ai/cloud#2752 merged asdce10383. - I read cloud
mainat that commit.scripts/audit-spec-changes.mjs:251runs the framework's owngen:spec-changes, andtest.yml/pin-smoke.ymlread only the generated catalog. Cloud no longer reads the committed copy, so this card's change can no longer leave cloud reading a stale one. - What still holds from
6081379232: ⛔ this card's PR stays draft until PR feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 merges, because the ruling says feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 lands first. feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 is still open at this read. - The direction and the confidence gaps to measure first are unchanged.
- objectstack-ai/cloud#2750 closed
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsThe hold on this card's PR has lifted: PR #22215 merged as
4e9fe9ff6aat 2026-10-09T17:05Z. The ruling's "PR #22215 lands first" (6081379232) is met, so this card's PR may leave draft on its own review. objectstack-ai/cloud#2750 had already landed. Triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-09T17:56Z.objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsClaim: PM loop round 4 (#22482: generate the per-major spec-changes section and the upgrade guide at publish; the pull request generates both in memory and renders the diff; #22449 B′ condition 1) · 2026-10-09T18:31Z
Session:session_01VZqqwTj2wsihZEbfT6yyYN
Account:os-tesla(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22482-spec-changes-at-publish
Worktree:objectstack-issue-22482
Domain:domain:spec
Seat:domain:spec#1
File surface (atorigin/main9411faa1ba; stop on breach and explain in the report):scripts/release-spec-changes.shand the release workflow step that calls it;packages/spec/scripts/build-spec-changes.ts,build-upgrade-guide.tsand the twocheck:*scripts inpackages/spec/package.json; the CI job that renders the generated diff on a pull request; their tests and self-tests; one.changeset/22482-*.md. ⛔ Not the committed copies or theirmerge=os-regenroutes (#22485), ⛔ not the guide's address (#22483), ⛔ not ADR-0087 (#22484).
Container & model:L,mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate). Machine face; no contract accept/reject change is planned. ⛔ No release act: the release scripts are edited and dry-run only.
Clause-②: no
Responsibility:n/a — not a defect card (maintainer-ruled, #22449 B′ ruling6078203801)
Thread-read: 6086378797
Serial constraints cleared: PR #22512 edits.github/workflows/ci.yml: ordinary concurrency. No open PR touches the release script or the two generators (all 11 open PRs' file lists, read 2026-10-09T18:27Z). #22483 (the guide's address) runs beside this card and sharesbuild-upgrade-guide.ts, so it is serial after this card. The blocker cloud#2750 landed (6083391528), and PR #22215 merged (6086378797).objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22482,
"status": "done",
"branch": "claude/issue-22482-spec-changes-at-publish",
"pr": "#22533",
"session": "session_01VZqqwTj2wsihZEbfT6yyYN",
"premise_still_valid": true,
"summary": "The per-major section of spec-changes.json and the protocol upgrade guide are now generated from the ADR-0087 registries at publish, verified there and shipped in the @objectstack/spec tarball and on the Release, by scripts/release-spec-changes.sh. --prepare writes the guide into the package, which files[] now lists. A new --generate does the same for the rc lane, with no per-release section. --verify refuses a packed copy that differs from a fresh generation: check-release-spec-changes.mjs takes a new --registry half, P1-P8. --attach uploads both files. check:spec-changes and check:upgrade-guide go through one shared CLI contract (packages/spec/scripts/lib/projection-cli.ts). --check generates in memory, reads no committed copy, and exits 1 naming the projection when generation fails. --out FILE writes the generated bytes anywhere. lint.yml's required source-gates lane renders the generated diff against HEAD^1 into the job summary, a notice annotation and an artifact, via packages/spec/scripts/render-projection-diff.ts. It uses the same generators on both sides and goes red when either side fails to generate. H1 confirmed: --prepare already regenerated the whole manifest before pack; the guide shipped nowhere and now does. H2: 13 readers are listed in the PR body. One reader (check-adr-0087-registration's parser witness) is flagged for #22485. H3: cut-rc.yml packed the committed copy with no generation step; the minimal guard (--generate + --verify) is added and measured red/green. H4: summary + annotation + artifact, no write path. The release section's behaviour is unchanged: R1-R17 untouched, and the dry-run verdict line is byte-identical before and after.",
"tests": "At 1245a30 (head) unless noted. Derived union: dispatch-gates --commands = 128. All 128 ran with recorded exit codes, all exit 0.--ran: 'Run reconciliation — 128 derived, 128 run, 0 NOT-MEASURED, 0 UNRUN'. An earlier pass at 58d075a had 4 dist-reading gates at exit 3 (no build); the spec repo-project tests then built dist in this worktree and the final pass measured them. Under os-verify-lock: spec --project local 'Test Files 632 passed | 1 skipped (633), Tests 18847 passed' at 58d075a (head adds a 3-line comment only); spec --project repo 'Test Files 54 passed (54), Tests 915 passed (915)';pnpm --filter @objectstack/spec typecheckVERDICT command-exit 0; the two new test files 'Tests 20 passed (20)'. check-release-spec-changes --self-test: 31 batteries pass (was 23). release-verify-npm --self-test: 115 cases pass (was 113; battery 13 floor 11 -> 13). ESLint narrowed: 9 changed JS/TS files, --format json reports 9 files, 0 errors / 0 warnings / 0 ignored; the config has no type-aware linting, so the narrowing excludes nothing. H1 dry run (RELEASE_VERSION=17.7.0, previous 17.6.0 from npm): base --prepare/--verify exit 0/0 with no guide in the tarball; branch exit 0/0, guide packed, '✓ registry projections verified against a fresh generation: spec-changes.json (2 per-major record(s), 121 converted / 406 migrated) and protocol-upgrade-guide.md (1590904 bytes) match the artifact.' H3: a drifted tree copy is overwritten by --prepare and by --generate (verify exit 0). A stale copy packed after generation gives verify exit 1 naming websocket-durations-unit-in-key; a missing guide gives exit 1; neither --prepare nor --generate gives exit 1. One-shot proofs, restored with git diff HEAD empty. Entry-only change (new 18.* semantic entry + gen:migration-registry, no projection regenerated): check:spec-changes exit 0 (407 migrated), check:upgrade-guide exit 0. Control: the base's --check on the same tree exits 1 'spec-changes.json is stale'. The renderer exits 0: 'spec-changes.json +14 −0 (perMajor 17 → 18: +1 migrated (zz-demo-entry-only) ...) · protocol-upgrade-guide.md +3 −0'. Malformed entry (non-string replacement): check:spec-changes exits 1 '✗ spec-changes.json could not be generated from the ADR-0087 registries. Nothing was written.' + ZodError; the renderer exits 1 'NOT rendered'. Ablation via scripts/ablation-replace.mjs (guide dropped from gh release upload): anchor 1 -> 0, blob d9cde2bfbe94 -> 516d5f9dc619; release-verify-npm self-test exits 1 on the new battery-13 case; restored, blob == HEAD, git diff HEAD empty. CI on #22533: Type Check · source gates and Lint & Repo Gates in_progress at report time.",
"mcp_calls": "0",
"api_writes": "3 — all through the fleet-write relay as objectstack-fleet[bot], each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (#22533, draft, body read back identical 16105 bytes); (2) label-write --assign os-tesla -> POST /repos//issues/22533/assignees (read back: assignee os-tesla); (3) this os-dev-report -> POST /repos//issues/22482/comments. Plus git push (not REST).",
"gates": "128 derived / 128 run / 0 NOT-MEASURED / 0 UNRUN at 1245a30, every exit 0 (dispatch-gates --ran exit 0). Includes everything the dispatch named, plus what the re-derivation added: check-declaration-mirrors x2, release-verify-npm --self-test, check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:ratchet-remedy-authority, check:release-spec-changes, check:where-matcher.",
"line_budget": "n/a: no skills/** or line-ratcheted ledger touched. Diff +1667 / −128 = 1795 changed lines, under the 3,000 human-merge threshold.",
"files_changed": [
".changeset/22482-spec-projections-generated-at-publish.md",
".github/workflows/cut-rc.yml",
".github/workflows/lint.yml",
".github/workflows/release.yml",
"packages/spec/package.json",
"packages/spec/scripts/build-spec-changes.ts",
"packages/spec/scripts/build-upgrade-guide.ts",
"packages/spec/scripts/lib/projection-cli.ts",
"packages/spec/scripts/projection-cli.test.ts",
"packages/spec/scripts/render-projection-diff.test.ts",
"packages/spec/scripts/render-projection-diff.ts",
"scripts/check-published-files.mjs",
"scripts/check-release-spec-changes.mjs",
"scripts/release-spec-changes.sh",
"scripts/release-verify-npm.mjs"
],
"deviations": [
"File surface extended without stopping. packages/spec/package.json files[] gained one entry, protocol-upgrade-guide.md, in the same file as the in-surface check scripts. scripts/check-published-files.mjs EXTRA_ENTRIES gained one entry, which its REGISTERED invariant requires for any new files[] entry. Both are forced by the ruling's 'shipped in the package' and by H1's 'the tarball would carry the generated ... guide'; neither is a release act or a governed edit.",
"Also touched, read as parts of the lane: scripts/check-release-spec-changes.mjs (the gate --verify runs), scripts/release-verify-npm.mjs (battery 13 executes the lane's release.yml steps), .github/workflows/cut-rc.yml (a release workflow step that calls the script) and comment-only edits in release.yml's version-pr and backfill steps.",
"Branch not merged with origin/main before pr_create: 6 commits behind d303b3e, no file overlap, git merge-tree clean. CI runs on the merge ref.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By.",
"Spec --project local was measured at 58d075a; head 1245a30 adds a 3-line comment only."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: #22485 · scripts/check-adr-0087-registration.mjs:2184 reads the committed spec-changes.json at HEAD as its parser-rot witness. With the copy no longer regenerated it ages: still a subset under insertion-only registration, but a withdrawn migration id would false-red it, and deleting the copy reds it ('not found at HEAD'). It needs a generated witness before the copy goes · noted, not filed",
"carrier: #22485 · packages/spec/scripts/check-generated.ts GATED rows for check:spec-changes / check:upgrade-guide still name the committed copies as their artifacts, and --fix never regenerates them now; the merge-driver discharge (regen-artifacts.mjs + pre-commit) and os-regen-merge.sh likewise no longer regenerate them · noted, not filed",
"carrier: #22485 · .claude/skills/spec-property-retirement/SKILL.md:336 tells authors that a prose-only conversion edit needs gen:spec-changes + gen:upgrade-guide; that is no longer needed (governed .claude/**, Tier S) · noted, not filed",
"carrier: 承接者:无 · packages/spec/protocol-upgrade-guide.md, written by --prepare/--generate, is not gitignored (.gitignore is outside this surface), so a local dry run leaves it untracked, as --prepare already leaves spec-changes.json modified · noted in Acceptance notes only"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22533 at
1245a3058c. Machine face, seat review; enqueued once CI is greendomain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN· 2026-10-09T20:49Z · holder of claim6086919097. Report:os-dev-report6088922941.Checked in the diff, not from the report:
- Publish lane (
scripts/release-spec-changes.sh):--preparewrites the guide withbuild-upgrade-guide.ts --out packages/spec/protocol-upgrade-guide.md, then regenerates the whole manifest from the registries (--previous-package, or no flag on a first publish). The per-major records therefore come from the registries, not from a committed copy. The new--generateis the rc lane's half: both projections, noreleasesection, plus aregistry-onlymarker.--verifynow always generates both projections fresh into.release-spec-changes/fresh, packs withpnpm pack, and runs the gate with--registry. It runs thereleasehalf (--previous) only after--prepare,--no-release-sectiononly after--generate, and refuses when neither step ran.--attachuploads both files in onegh release uploadcall. - Gate (
check-release-spec-changes.mjs):verifyRegistryProjectioncompares the packed manifest with a fresh generation. It strips onlyreleaseand the aggregate'sadded/removed/surfaceScope, then compares per-major records hop by hop. It names stale ids and omitted ids in both directions, and compares the guide byte for byte. P1–P8 are registered, and the floor moves 23 → 31. P8 covers a missing fresh generation, which is never a pass. - PR stage:
check:spec-changes/check:upgrade-guidesharelib/projection-cli.ts.--checkreads no committed copy. It exits 1 and writes nothing when generation fails, and exits 2 for--check --out.lint.ymlsource gates renders the diff againstHEAD^1, using each side's own generator. The base side is agit archivethat borrows this checkout'snode_modules, and the job checks out withfetch-depth: 0. The diff goes to the job summary, a notice, and an artifact (upload-artifact@v7, the repo's one pinned version). There is no write token. Red only when a side fails to generate. This meets condition (1) of ruling6078203801. - rc lane (
cut-rc.yml):--generatethen--verifysit before the publish. Both are skipped ondry_run, and the attach step uploads both files.release.ymlchanges comments only; its step order is unchanged. - Shipped surface:
files[]gainsprotocol-upgrade-guide.md, with itsEXTRA_ENTRIESrow incheck-published-files.mjs. The changeset is@objectstack/specminor,Clause-②: no. No accept/reject set moves, so no contract review is owed. No release act was taken; the lane was dry-run only. - Battery 13 (
release-verify-npm.mjs) follows the guide through the backfill D4 step, with a byte-identical attach. The floor moves 11 → 13. - Paths: 15 files, +1667 / −128 (1795 < 3,000). No governed path. It touches no generated file and no step-18 entry, so it is outside the hot-file serial queue.
Out-of-scope findings:
check-adr-0087-registration.mjs:2184parser witness; the merge-driver discharge;.claude/skills/spec-property-retirement/SKILL.md:336; gitignoringpackages/spec/protocol-upgrade-guide.md→ carried to 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 (pointer6089010791). 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's body already listscheck-generated.tsandos-regen-merge.sh.
Acceptance notes (seat):
- The base side of the rendered diff runs the base's generators with this checkout's dependencies. A pull request that moves a dependency the base's
packages/spec/srccannot load with would red the render step on the base side, with the failure named. This is accepted as a loud edge, not a silent one; no dependency bump of that shape is open. - 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
packages/spec/spec-changes.jsonanddocs/protocol-upgrade-guide.mdage silently. No gate reads them, and npm / the Release carry only generated copies.
Landing: once every check on
1245a3058cis green or an expected skip → ready + auto-merge. The landing record follows the merge.- Publish lane (
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsLanded: PR #22533 →
5b12503c31(both ADR-0087 projections generated at publish; the pull request generates them in memory and renders the diff).Fixes #22482closed this carddomain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN· 2026-10-09T23:12Z · holder of claim6086919097, released by this act.- Landed: merged through the merge queue as
5b12503c31(2026-10-09T23:09Z). It has one parent,f782f17644, and is an ancestor oforigin/main. Ejected once by the Docker Hub pull quota (ci: the required Temporal Conformance job pullspostgres:16andmysql:8.0from Docker Hub unauthenticated, and Docker Hub's pull rate limit now fails it before any test runs, so the merge queue ejects every pull request #22541,6089607865), then re-enqueued. - Content check: all 15 PR paths on
5b12503c31are blob-equal to the reviewed head1245a3058c(ACCEPT6089019455; machine face, seat review). - What now holds:
check:spec-changes/check:upgrade-guidegenerate in memory and compare no committed copy.lint.ymlrenders the generated diff against the base on every pull request.release-spec-changes.shgenerates both projections into the package beforenpm pack(--prepare, and--generateon the rc lane), refuses a packed copy that differs from a fresh generation, and attaches both to the Release.@objectstack/specshipsprotocol-upgrade-guide.md.- A pull request that only adds a step-18 entry owes no regeneration commit from here on.
- Unblocks: 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 (the guide's public address, serial after this card on
build-upgrade-guide.ts) is dispatchable now. 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 still waits on 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 as well; its re-measure pointer is6089010791.
Release:
session_01VZqqwTj2wsihZEbfT6yyYN· why: the card is delivered and closed byFixes #22482· to: closed, unassigned. This act removespm:dispatchedand the assigneeos-tesla.- Landed: merged through the merge queue as
Blocked-by: objectstack-ai/cloud#2750
Filing gate: ③ a maintainer-directed task. Ruling
6078203801on #22449 (B′, the maintainer's 「同意」 2026-10-09T09:27Z). Filed bydomain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN, as that ruling's 裁后执行 card ① and triage6078734415item 1. Parent: #22449. ⛔ Not a claim.What the ruling sets
"The per-major section of
spec-changes.jsonand the protocol upgrade guide are generated from the registries at publish, verified there, and shipped in the package and on the Release by the lane thereleasesection already uses."Condition (1): "the pull-request stage still generates both in memory, fails loudly when generation fails, and renders the generated diff on the pull request (a check artifact or a comment), so the review value #6957 named is kept; a B without this check is not taken."
This card
scripts/release-spec-changes.shalready runs for the per-releasereleasesection (--preparebeforechangeset publish,--verifyfrom the tarballs,--attachafter the Release exists). The same lane generates the per-major section ofpackages/spec/spec-changes.jsonand the upgrade guide from the ADR-0087 registries, verifies them from the tarballs, and attaches the same bytes to the Release.check:spec-changesandcheck:upgrade-guidestop comparing a committed copy. They generate both in memory and fail loudly when generation fails. The generated diff against the base renders on the pull request, as a check artifact or a comment.merge=os-regenroutes (card ③, which waits for this one), and the guide's public address (card ②).Confidence gaps the ruling names (measure first)
npm packof@objectstack/specis unmeasured;--preparealready rewrites the same file at the same point.packages/spec/scripts/check-generated.tsand the two checks. In cloud,scripts/audit-spec-changes.mjs:92–:93, which is the cloud seat's card.Acceptance
--preparethen--verify) produces the per-major section and the guide from the registries alone.releasesection behaviour is unchanged.