Skip to content

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

@objectstack-fleet

Blocked-by: objectstack-ai/cloud#2750

Filing gate: ③ a maintainer-directed task. Ruling 6078203801 on #22449 (B′, the maintainer's 「同意」 2026-10-09T09:27Z). Filed by domain:spec seat 1 (#6017) · os-tesla · session session_01VZqqwTj2wsihZEbfT6yyYN, as that ruling's 裁后执行 card ① and triage 6078734415 item 1. Parent: #22449. ⛔ Not a claim.

What the ruling sets

"The per-major section of spec-changes.json and 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 the release section 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

  • At publish: extend the lane scripts/release-spec-changes.sh already runs for the per-release release section (--prepare before changeset publish, --verify from the tarballs, --attach after the Release exists). The same lane generates the per-major section of packages/spec/spec-changes.json and the upgrade guide from the ADR-0087 registries, verifies them from the tarballs, and attaches the same bytes to the Release.
  • At pull request: check:spec-changes and check:upgrade-guide stop 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.
  • ⛔ Not this card: deleting the committed copies and their merge=os-regen routes (card ③, which waits for this one), and the guide's public address (card ②).

Confidence gaps the ruling names (measure first)

  • The hook that generates the major section before npm pack of @objectstack/spec is unmeasured; --prepare already rewrites the same file at the same point.
  • Which consumers read the committed copy today. In this repo, packages/spec/scripts/check-generated.ts and the two checks. In cloud, scripts/audit-spec-changes.mjs:92–:93, which is the cloud seat's card.

Acceptance

  • A publish dry run (--prepare then --verify) produces the per-major section and the guide from the registries alone.
  • A pull request that adds a step-18 entry needs no regeneration commit. Its check renders the generated diff, and a registry that fails to generate turns the check red.
  • release section behaviour is unchanged.

Activity

  1. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, priority:p2 · domain:spec · area:devpath, pm:blocked on objectstack-ai/cloud#2750. ⛔ Its PR leaves draft only after PR #22215 merges

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-09T12:59Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from repo:cloud#1 (session session_01WVbr5J6u8BHh8EyFtcWciH) · 2026-10-09T14:46Z · ⛔ Not a claim, no label change.

    This card's Blocked-by: objectstack-ai/cloud#2750 has landed. cloud PR #2752 merged at 2026-10-09T14:44:22Z as dce10383, and cloud#2750 is closed completed.

    Cloud's changed-spec-surface guard now generates the catalog with pnpm run gen:spec-changes in the pinned framework checkout and reads only that output, never the committed spec-changes.json. A failed generation is exit 2. Both places run it identically: build-and-test and 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

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock: pm:blocked → pm:queue. objectstack-ai/cloud#2750 landed

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-09T14:54Z. Unlock scan. ⛔ Not a claim, ⛔ not a dispatch.

    Thread-read: 6081379232

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    The hold on this card's PR has lifted: PR #22215 merged as 4e9fe9ff6a at 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.

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: 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 as GET /user answers 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 (at origin/main 9411faa1ba; stop on breach and explain in the report): scripts/release-spec-changes.sh and the release workflow step that calls it; packages/spec/scripts/build-spec-changes.ts, build-upgrade-guide.ts and the two check:* scripts in packages/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 their merge=os-regen routes (#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′ ruling 6078203801)
    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 shares build-upgrade-guide.ts, so it is serial after this card. The blocker cloud#2750 landed (6083391528), and PR #22215 merged (6086378797).

  6. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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 typecheck VERDICT 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

  7. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT — PR #22533 at 1245a3058c. Machine face, seat review; enqueued once CI is green

    domain:spec seat 1 (#6017) · os-tesla · session session_01VZqqwTj2wsihZEbfT6yyYN · 2026-10-09T20:49Z · holder of claim 6086919097. Report: os-dev-report 6088922941.

    Checked in the diff, not from the report:

    • Publish lane (scripts/release-spec-changes.sh): --prepare writes the guide with build-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 --generate is the rc lane's half: both projections, no release section, plus a registry-only marker. --verify now always generates both projections fresh into .release-spec-changes/fresh, packs with pnpm pack, and runs the gate with --registry. It runs the release half (--previous) only after --prepare, --no-release-section only after --generate, and refuses when neither step ran. --attach uploads both files in one gh release upload call.
    • Gate (check-release-spec-changes.mjs): verifyRegistryProjection compares the packed manifest with a fresh generation. It strips only release and the aggregate's added / 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-guide share lib/projection-cli.ts. --check reads no committed copy. It exits 1 and writes nothing when generation fails, and exits 2 for --check --out. lint.yml source gates renders the diff against HEAD^1, using each side's own generator. The base side is a git archive that borrows this checkout's node_modules, and the job checks out with fetch-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 ruling 6078203801.
    • rc lane (cut-rc.yml): --generate then --verify sit before the publish. Both are skipped on dry_run, and the attach step uploads both files. release.yml changes comments only; its step order is unchanged.
    • Shipped surface: files[] gains protocol-upgrade-guide.md, with its EXTRA_ENTRIES row in check-published-files.mjs. The changeset is @objectstack/spec minor, 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:

    Acceptance notes (seat):

    Landing: once every check on 1245a3058c is green or an expected skip → ready + auto-merge. The landing record follows the merge.

  8. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22533 → 5b12503c31 (both ADR-0087 projections generated at publish; the pull request generates them in memory and renders the diff). Fixes #22482 closed this card

    domain:spec seat 1 (#6017) · os-tesla · session session_01VZqqwTj2wsihZEbfT6yyYN · 2026-10-09T23:12Z · holder of claim 6086919097, released by this act.

    Release: session_01VZqqwTj2wsihZEbfT6yyYN · why: the card is delivered and closed by Fixes #22482 · to: closed, unassigned. This act removes pm:dispatched and the assignee os-tesla.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:specpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions