Skip to content

build(spec): the migration registry is generated at build and leaves git - #22706

Merged
objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/issue-22554-registry-build-time
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/issue-22554-registry-build-time

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22554

Clause-②: no

Executes ruling B on #22554 (comment 6092692730, maintainer 「同意」), which completes #22449 B′ (comment 6078203801): packages/spec/src/migrations/registry.ts is generated at build and leaves git, at its unchanged path. The 3 source imports, the script import and the test imports are untouched. The published package is unchanged apart from package.json scripts and the input-hash stamps (measured below).

Landing: +4,054 / −31,059 across 25 files, about 35,100 changed lines (GitHub's reading at head 81ae60b20a). That is over the 3,000-line line, so this PR takes the maintainer's APPROVED review, as the ruling notes. It stays draft. The two PM-tool fixes it needs co-land here (see "Two PM tools, co-landed here").

A premise the ruling stated, measured false: the registry was a MIXED file

The ruling says "the entry files stay the only source". At cb3bb9333f, registry.ts had 30,718 lines (the ruling's 30,124 is an earlier count). Of these, 27,268 sit inside the six os-generated regions, concatenated from 899 entry files (419 semantic, 258 retired-key, 222 retired-def). The other 3,451 lines are hand-written: the module header, the two value imports, MIGRATION_SUPPORT_FLOOR, step 17's rationale, STEP18_RATIONALE (about 1,937 lines of keyed fragments), step18, MIGRATIONS_BY_MAJOR, MIGRATION_MAJORS and the two tables' doc comments. The generator used the file as its own template.

So the hand-written part needs a committed home. In the ruling's intent, it becomes packages/spec/src/migrations/registry.ts.template: the old file with every region emptied, produced mechanically. The generator now writes registry.ts whole from that template plus entries/. Regenerating from an absent file reproduced the committed blob byte for byte (feb4843913) before the header rewording below.

The template is deliberately not a .ts file, for two reasons. With empty regions, its TranslationDataSchema import is unused, and the root tsconfig's noUnusedLocals would red tsc. More important, a .ts template would be importable, and an import of it would silently receive empty tables.

Measured cost of this to the ruling's "no shared file for a D3 PR": of the last 60 commits that touched registry.ts, 33 also changed hand-written lines, mostly one STEP18_RATIONALE fragment. Those edits now land in the template. It is keyed and sorted, so two retirements insert at different lines (#20535, pinned by step18-rationale-merge.test.ts, which now merges the template). Region-only PRs (the other 27 of 60) touch no shared file at all.

The ruling's measure-first readings (condition 2)

  1. Generation wall time. Measured at BASE with tsx scripts/build-migration-registry.ts --self-test --check and a TMPDIR-isolated tsx cache, load about 3.3 on 4 cores. Cold runs: 1,808 / 1,583 / 1,685 ms. Warm runs: 1,611 / 1,426 / 1,563 ms. pnpm run gen:migration-registry took 1,465 ms. A fresh pnpm install --frozen-lockfile including the generation took 11 s.
  2. prepare on a workspace package runs on pnpm install. I measured this on a fresh worktree with an uncommitted probe prepare on @objectstack/spec. pnpm 10.31.0 printed packages/spec prepare$ … and wrote the marker with cwd packages/spec. The probe was then restored byte-identical (blob de5c9fa2dd, git diff HEAD empty). The real prepare (pnpm gen:migration-registry) was then measured on a second fresh worktree at 765ae6bde4. Install printed ✓ wrote src/migrations/registry.ts (419 semantic, 258 retired-key, 222 retired-def) and git status stayed clean. The editor is therefore not red before the first build. prepare is strict: a malformed entry fails the install with the generator's located message. A lenient one would leave the previous run's file on disk to be read stale. Also measured: pnpm pack runs prepare and leaves it out of the packed manifest.
  3. typecheck order. The script is now pnpm gen:migration-registry && tsc --noEmit && pnpm check:scripts-typecheck && pnpm check:test-typecheck, so all three run after generation. turbo's @objectstack/spec#typecheck also depends on the generation task. In the fresh worktree the typecheck went green with no dist/ present.

What changed

Readers of the committed copy, adapted

A census of git grep migrations/registry (112 files) plus a run of every derived gate found these path-dependent readers. Each is changed only as far as the retirement requires.

  • scripts/check-adr-0087-registration.mjs read the ledger with git show REV:…/registry.ts and laid out its witness with git archive, so it went red ("ledger source not found at HEAD").
    • ledgerAt now reads a rev's migration ledger from the template plus the entry files when that rev tracks the template, and from the committed registry otherwise, because a merge base can predate this PR. Measured on HEAD against cb3bb9333f: 532 / 591 ids on both sides, with 0 differences either way.
    • The witness runs the rev's own registry generator before projecting.
    • The --audit-stock ledger-touch test counts entry and template commits.
    • A new battery, GR1–GR7 (13 cases), covers the generated era, including the committed-to-generated transition merge. The roster floor is 50 → 51.
  • scripts/check-future-spec-major.mjs: its exemption row for registry.ts matched nothing once the file left the tracked corpus. The row is dropped; the entry file that carries the sentence keeps its own row, and the R2 self-test fixture is re-pointed at it.
  • packages/spec/scripts/check-generated.ts: gen:migration-registry moves to UNGATED_GENERATORS, with the build-output rationale gen:openapi already has.
  • scripts/regen-artifacts.mjs: the registry's NOT_DRIVER_MANAGED row becomes untracked: true (git never merges it).
  • .gitattributes, entries/README.md, the build-schemas.ts remedies and test comments: stop naming the retired check or a committed lap.
  • Disk readers stay green. The generated file exists after every install, so check:dispatcher-error-vocabulary's row and the ESLint stack-headroom canary still see it. check:cli-command-ids, check:issue-citations and check:cross-package-test-inputs were also measured green.

The published package

pnpm pack of @objectstack/spec built at BASE cb3bb9333f was compared with the build at HEAD (merge 625c4d202e; the later commit only adds the changeset). Both tarballs hold 2,070 files, and 4 differ:

  • package/package.json, in scripts only: build, typecheck, test and test:repo start with the generation, and check:migration-registry is gone. prepare is not in the packed manifest.
  • dist/.build-input-hash, dist/.build-input-hash-dts and json-schema/.build-input-hash-schema (53a504fa… to ac4356b2…). They hash the build inputs, which this PR changes by construction.

Every dist JS file, declaration, source map and JSON Schema, and every shipped src/**/*.zod.ts, is byte-identical. Tarball sha256: BASE d3d59ac0…, HEAD 362a845e…. pnpm check:published-files passed at HEAD 765ae6bde4.

A fresh checkout builds, typechecks and tests with no committed registry

This ran on a new detached worktree at 765ae6bde4, where registry.ts was absent:

  • pnpm install --frozen-lockfile: prepare generated the file.
  • pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0, before any build.
  • pnpm --filter @objectstack/spec build: VERDICT command-exit 0, with input hash ac4356b2…, identical to the primary worktree's build.
  • registry.ts deleted, then pnpm --filter @objectstack/spec test: the script wrote it again (✓ wrote src/migrations/registry.ts). Result: Test Files 642 passed (642), Tests 19164 passed | 1 todo (19165), VERDICT command-exit 0.

Two PM tools, co-landed here (scripts/pm, declared to the domain:skills seat on #7623)

  • scripts/pm/os-regen-merge.sh: self-test case 9c pinned the registry as a TRACKED NOT_DRIVER_MANAGED row, which is untracked now. It pins skills/README.md / gen:skill-docs / skills/README.md: merge: unspecified instead, the same shape (tracked, MIXED, marked regions, a generator); all cases pass. The header's class-3 roster (about :359–:366) now names two MIXED rows, with the registry noted as the former third. Comment text only.

A touch on the frozen scripts/pm/dispatch-gates.mjs, under ruling 208's exception

Ruling 208 R6, as .claude/skills/pm-dispatch/references/instrument-discipline.md carries it: 「工具位只有一个,先花在删除上;dispatch-gates.mjs 冻结,只在它喂的 workflow 坏了时碰」. This PR makes packages/spec/src/migrations/registry.ts generated and git-ignored, so the hint a gate's import of the registry yields names a file no card can touch. Two things broke as a result: check:pm-dispatch-gates, a Lint & Repo Gates leg, went red on its extension-narrowing agreement case; and a card editing only an entry file or the template stopped deriving five families that import the registry (check:spec-changes, check:upgrade-guide, check:authorable-surface, check:query-options-erasure, check:role-word). That is the workflow the file feeds breaking, so the file is touched under the ruling's exception, in the W3 split's shape:

  • Data: scripts/pm/dispatch-gates.data.mjs, +43/-2. The declared row GENERATED_MODULE_SOURCES (the registry module, and its committed sources registry.ts.template and entries/) with its rationale.
  • Engine: scripts/pm/dispatch-gates.mjs, +11/-1 against 765ae6bde4. The data import and re-export, a five-line generatedModuleSources lookup and one || clause in hintCovers.
  • Self-test: scripts/pm/dispatch-gates.self-test.mjs. It pins the mapping with a control, proves the row against the tree on every run (module untracked and ignored by a tracked ignore file, sources tracked), and keeps mapped hints out of the extension-narrowing agreement case. One census line pin moved 734 -> 736 with the import. Removing the clause turns exactly the mapping case red (1 of 1828).

Declared to the domain:skills seat on #7623; that seat's answer is comment 6100894139 on #22554.

Governed text this makes false (reported, not edited)

.claude/skills/spec-property-retirement/SKILL.md:213 says the D3 step lives in packages/spec/src/migrations/registry.ts, and that a step-18 retirement adds its STEP18_RATIONALE fragment and an earlier step's conversionIds and rationale there. After this PR those hand edits belong in registry.ts.template: an edit to registry.ts is never committed. The proposed wording is in the os-dev report.

Concurrency

Verification at HEAD 84612b393b (patch round) and 765ae6bde4 (round 1)

  • Patch round, at 84612b393b (81ae60b20a adds only the os-regen-merge.sh header comment; its self-test reads "all cases pass" there): pnpm --filter @objectstack/spec typecheck VERDICT 0; pnpm --filter @objectstack/spec test VERDICT 0, Test Files 642 passed (642), Tests 19197 passed | 1 todo (19198); pnpm check:pm-dispatch-gates "1828 cases pass" and check-dispatch-gates.mjs --slow "2088 cases pass"; os-regen-merge.sh --self-test "all cases pass"; the gate union re-derived on the 25 paths, 146 commands, "146 derived, 146 run, 0 NOT-MEASURED, 0 UNRUN", all exit 0 (including check:dual-build-cjs-loads, measured this time). CI on 84612b393b: 34 success, 2 skipped. The bullets below are round 1's.

  • @objectstack/spec: typecheck, build and test, all green (VERDICT command-exit 0 each). These ran on the fresh worktree above, at this commit, under scripts/pm/os-verify-lock.sh.

  • Gates: the union node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives from the 21 changed paths is 146 commands, all run at this commit with exit codes captured before any pipe:

    • 144 green. One of them, check:dts-closure, first exited 1, naming the half-built dist/ of two plugins that my own earlier gate runner had left when its timeout killed check:type-check-debt mid-build. I removed those git-ignored partial trees and reran it: exit 0, 171/171 declaration files across 71 packages.
    • 1 red: check:pm-dispatch-gates (above).
    • 1 NOT MEASURED: check:dual-build-cjs-loads, which exited 3 because it needs every workspace package built. This PR changes no package's dist, and the spec tarball diff above shows its own dist is byte-identical.
  • dispatch-gates --ran over the exit-coded record: "146 derived, 145 run, 1 NOT-MEASURED, 0 UNRUN".

  • Self-tests run directly:

    • node scripts/check-adr-0087-registration.mjs --self-test: 463 assertions.
    • bash scripts/ci/select-gate-families.selftest.sh: 59 cases, 304 checks.
    • node scripts/regen-artifacts.mjs --self-test and node scripts/git-merge-regen.mjs --self-test: exit 0.
    • tsx packages/spec/scripts/build-migration-registry.ts --self-test: ok.
  • ESLint, narrowed to the changed lintable files plus the generated registry.ts: 11 files, 0 errors, 0 warnings, 0 fatal, run with the lint script's own --stack-size=4000 --no-inline-config.

    • Population: read from eslint.config.mjs, whose objects glob **/*.{ts,…}. --print-config on registry.ts.template answers undefined, so the template is outside it.
    • Invariance: no parserOptions.project (no type-aware rules), so this diff cannot move a verdict on an untouched file. The repo-wide run is CI's.

Ablations (one-time proofs, each restored to the HEAD blob under the tool's own trap)

All were run through node scripts/ablation-replace.mjs on the committed tree.

  1. migrationLedgerTextAt forced to the committed era: the ADR-0087 self-test goes red on GR1, GR3 and both GR7 cases.
  2. The witness's registry-generation step disabled: the real gate against origin/main goes red, with "Cannot find module '../src/migrations/registry'" in the witness.
  3. The entry comparator swapped for localeCompare: the generator's self-test goes red on the new ordering case.

Acceptance notes

  • scripts/regen-artifacts.mjs cites .gitignore:61, :73 and :108 for three other untracked rows. These were already off by 2 before this PR, and the six lines added here move two of them further. They are not gated. Noted, not changed.
  • The packages/spec/src/migrations/registry.ts path keeps its name and exports, so export-origins/migrations.json, SYNC_ARCHITECTURE.md and the source comments that name it stay true.

Generated by Claude Code

packages/spec/src/migrations/registry.ts is now generated whole and
git-ignored, at its unchanged path. gen:migration-registry writes it from
the committed hand-written skeleton src/migrations/registry.ts.template
(everything outside the os-generated markers, regions empty) and the
per-entry files under src/migrations/entries/. It runs on pnpm install
(the package's prepare), as the first step of build, and before
typecheck, test and test:repo; turbo declares it as their prerequisite
with the file as the task output. The generator's self-test runs inside
every generation and now also covers ordering and determinism.
check:migration-registry retires: there is no committed copy to compare.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
…e generated registry

The registry's committed copy is gone, so every reader of it moves to its
two committed sources:

- check-adr-0087-registration reads a rev's migration ledger from the
  template and the entry files when the rev tracks the template (the
  committed registry otherwise, for merge bases from before), and its
  witness runs the rev's own registry generator before projecting. A new
  GR battery covers the generated era, including the transition merge.
- check-future-spec-major drops its exemption row for the registry; the
  entry file that carries the sentence keeps its own.
- check-generated records gen:migration-registry as an ungated generator
  of git-ignored build output; regen-artifacts marks the registry row
  untracked.
- The lint.yml step and the migration_registry gate family retire, with
  the selector's self-test pins for that family (its floor drops to the
  measured 59 cases / 304 checks).
- build-schemas remedies, the entries README, .gitattributes and test
  comments stop naming the retired check; step18-rationale-merge merges
  the template, the file git now merges.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Oct 10, 2026
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 3 changed file(s) yielded no anchor (packages/spec/src/migrations/entries/README.md, packages/spec/src/migrations/registry.ts, packages/spec/src/migrations/registry.ts.template), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/src/migrations/entries/README.md, packages/spec/src/migrations/registry.ts, packages/spec/src/migrations/registry.ts.template) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0f77ff5202d8ed3b18febce4130ccadf269719a7 → packageMentionDocs.

The registry left git in this PR, which reddened two scripts/pm self-tests on
Lint & Repo Gates. Co-landed here, declared to the domain:skills seat:

- dispatch-gates: an import hint on a generated, git-ignored module reaches
  that module's committed sources. GENERATED_MODULE_SOURCES declares one row,
  the registry and its template plus entries/, because only its turbo task
  names the sources and those inputs also name the manifest and the
  generator. The self-test pins the mapping with a control, proves the row
  against the tree (module untracked and ignored by a tracked ignore file,
  sources tracked), and keeps such hints out of the extension-narrowing
  agreement population.
- os-regen-merge self-test case 9c pins skills/README.md and gen:skill-docs:
  the registry row it pinned is untracked now, and the reader skips those.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
dispatch-gates.mjs is frozen (ruling 208 R6), and this PR touches it only
under that ruling's exception: the workflow it feeds broke. As the W3 split
did, the declared row GENERATED_MODULE_SOURCES and its rationale move to the
data file, and the engine keeps only what matches it. That is the import and
re-export, a five-line lookup and the hintCovers clause: +11/-1 against
765ae6b.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
The engine's inherited-population marker sits two lines lower (734 -> 736)
because GENERATED_MODULE_SOURCES joined the data import and the re-export
above it. Same marker, same text.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
…gistry-build-time

# Conflicts:
#	packages/spec/src/migrations/registry.ts
The migration registry was the third until this PR made it git-ignored
build output, an untracked row the reader skips. Comment text only.

Claude-Session: https://claude.ai/code/session_01S3aAf11JjbW1mSGL1EhfFj
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 81ae60b20a023a45b0489373110624310a5d2e60
Local-runs: none

Inputs read: card #22554 (body and all 10 comments, rulings 6092692730 on this card and 6078203801 on #22449 included), PR #22706 (body, 25-file list, net diff origin/main...claude/issue-22554-registry-build-time at 81ae60b20a, merge base f66fdc7973), the check-runs on the head. Nothing built, run or re-run; the template was judged by git show of both blobs and a text diff. Seat comments on the card thread were read as claims.

① Derived judgments

Ruling B's mechanics, each against the diff

The premise "the entry files stay the only source", measured false, and the template

  • The ruling's premise does not hold on main: with the six os-generated regions emptied, main's registry.ts leaves 3,450 lines of hand-written skeleton (header, two value imports, MIGRATION_SUPPORT_FLOOR, step 17's rationale, STEP18_RATIONALE, step18, the tables and their doc comments). The dev's adaptation — a committed registry.ts.template — is the right one and inside the ruling's intent (generate at build, out of git, path unchanged); it is not a new fork.
  • Fidelity — verified. git show origin/main:.../registry.ts with each region's body removed (open and close markers kept) is byte-identical to the branch's registry.ts.template except lines 19, 21–26 and 38–42, the declared header rewording; 3,450 lines on both sides, so the generated file keeps its line count and the shipped source maps cannot move. The regions are the generator's: entries identical at the merge base, the branch and origin/main (419 semantic / 258 retired-key / 222 retired-def; only entries/README.md differs, by this PR's prose).
  • Importable by accident — no. The extension is .template: no module resolver (tsc, tsx, vitest, Node) loads it; it is outside the root tsconfig's *.ts include and noUnusedLocals (its TranslationDataSchema import is unused with empty regions), outside eslint.config.mjs's **/*.ts objects, and outside files[] (src/**/*.zod.ts is the only src pattern), so it neither typechecks as a stub nor ships. MODULE_SPECIFIER_EXTENSIONS does not include it either, which is why dispatch-gates needed the declared row rather than an extension match.
  • Byte-identical published package — met apart from the four declared differences, and those are inherent to the ruling's own mechanics. package.json ships its scripts, so build / typecheck / test / test:repo gaining the generation and check:migration-registry leaving are the ruling's changes made visible in the manifest; the three .build-input-hash stamps hash the inputs this PR changes by construction. Every dist file, declaration, map, JSON Schema and *.zod.ts unchanged is a claim consistent with the diff: no runtime source, zod file or export moves. prepare in the published manifest — does not leak: the release lane is changeset publish → pnpm publish --no-git-checks (release.yml:1792), and pnpm's exportable manifest strips prepare (the dev's pnpm pack measurement agrees). check:published-files (lint.yml:4523) keeps files[] a whitelist and is in the in-progress Lint & Repo Gates run.
  • prepare on install — a new install-time behaviour: every pnpm install runs the generator for the spec package and a malformed entry fails the install, loudly. Right: a lenient prepare would leave a stale file to be read. Every CI install on the branch is pnpm install --frozen-lockfile with no --ignore-scripts (lint.yml :279, :5296, :6122, :6351, :6606; ci.yml ×7), so the disk readers that never build (check:dispatcher-error-vocabulary, the ESLint canary, check-adr-0087-registration) see the file.

Every reader of the committed copy, each change judged — none weakens a gate beyond the retired family

  • scripts/check-adr-0087-registration.mjs (+195/−4) — right. migrationLedgerTextAt(rev) reads the ledger from the template plus every .ts under the three entry directories when the rev tracks the template (the template wins over a stale tracked registry), from the committed registry otherwise (merge bases before this PR), and null when neither — which assertInputs reports as red naming both paths, never an empty ledger. The witness runs the rev's own build-migration-registry.ts first; a template with no generator beside it, or a generator that exits non-zero, is red quoting it. ledgerTouchingCommits counts entry and template commits in the generated era. New battery GR1–GR7 (13 asserts) incl. GR2 (README id-shaped lines are not ids) and GR7 (the committed→generated transition merge: carried entry not new, added entry new); SELF_TEST_BATTERY_FLOOR 50→51. showManyOrNull, showOrNull exist on the branch.
  • scripts/check-future-spec-major.mjs (+2/−12) — right. The exemption row for registry.ts would match nothing once the file left the tracked corpus; it is dropped, the entry file that carries the sentence keeps its own row, and the R2 fixture is re-pointed at QUOTATION_EXEMPTIONS[3], which on the branch is that entry file's row (verified by index). No exemption is widened.
  • packages/spec/scripts/check-generated.ts (+13/−10) — right. The GATED row goes with its check; gen:migration-registry joins UNGATED_GENERATORS with a rationale stronger than gen:openapi's; the --reconcile-only meta-gate (lint.yml:5565) still requires every gen: to be classified.
  • scripts/regen-artifacts.mjs (+16/−14) — right. The registry's NOT_DRIVER_MANAGED row becomes untracked: true with the same expiry clause as the other untracked rows (committing it again reds).
  • scripts/ci/select-gate-families.sh (+6/−37) and its selftest (+15/−116) — right, and bounded. Only the migration_registry arm and family leave; no other family's read-set changes. The selftest loses exactly the 12 cases of the retired family (counted in the diff) plus the expect_reason migration_registry lines of retained cases and its pin_step; the "N run, M skipped" totals drop the family from skipped; ALL matches the nine if: ids left in lint.yml. Floor 71/369 → 59/304 is the dev's measured count; the check is -lt, so a tighter battery still passes.
  • .gitattributes (comment), entries/README.md, build-schemas.ts remedy strings, build-schemas-check-mode.test.ts, build-migration-registry-entry.test.ts, migrations.test.ts — prose only, each now true. entries/README.md tells a step-18 retirement to put its STEP18_RATIONALE fragment in the template ([finding] migrations/registry.ts: every major-18 retirement PR rewrites the closing line of step18.rationale, so any two in flight conflict in GitHub's merge #20535 ordering kept).
  • lint.yml (+9/−21) — step and if: removed; "NINE scoped families" comment matches FAMILIES.

The scripts/pm co-landing, against the skills seat's two conditions (6100894139)

  • Condition 1 (W3 shape, about a dozen engine lines) — met. dispatch-gates.data.mjs +43/−2 carries the declared row GENERATED_MODULE_SOURCES (module → registry.ts.template + entries) with its rationale and the declared-not-derived argument. dispatch-gates.mjs +11/−1 exactly: the import, the re-export, the five-line generatedModuleSources lookup with its one-line docblock, and one || clause in hintCovers (file or anything under a source directory; not a sibling, not the generator, not the manifest). dispatch-gates.self-test.mjs +56/−2: the mapping case with both hint spellings, a control (sibling module, lookalike directory, another module's hint), a per-run tree proof (module untracked and ignored by a tracked ignore file, every source tracked), the agreement-case exclusion, and the census pin 734 → 736. ignoreVerdicts, trackedFiles, trackedPrefixes exist on the branch. os-regen-merge.sh case 9c re-pointed at skills/README.md / gen:skill-docs / merge: unspecified, as that seat accepted; 81ae60b20a adds only the class-3 roster comment (two MIXED rows, the registry named as the former third).
  • Condition 2 (the approval sees it) — half met in the PR body: the section "A touch on the frozen scripts/pm/dispatch-gates.mjs, under ruling 208's exception" quotes the exception line beside the hunk and names the declaration on [PM seat] domain:skills — ⏳ vacant #7623. The other half — the 维护者速读's 你要做的 naming the touch as part of what the APPROVED review accepts — is not in the PR body (it has no 维护者速读); it is the seat's hand-off summary. Carried to ③.

The merge of #19939 S1 (f66fdc7) — git rm was lossless. git show f66fdc7973 -- registry.ts: 3 hunks, +16/−4, at old lines 14477–14494, 14505–14519, 14520–14538, all entry-literal lines of flow-value-slot-template-dialect-refused; the semantic:18 region spans 6981–24100 on the parent and 6981–24112 after, so every hunk is inside the markers. The branch's entries equal the merge base's, and origin/main's registry blob is still 9aaae83bde (unchanged since f66fdc7). The claude/issue-15204-s1-position-permission-sets branch is region-only per the dev — a claim, with GR7 now the gate for that merge shape.

Accept-set and public-surface changes the diff implies — none. No schema, export, authorable key, runtime path or JSON Schema moves. Repository surfaces that move, each right: the check:migration-registry script and migration_registry family cease to exist; registry.ts is untracked; registry.ts.template is tracked; gen:migration-registry loses --check and always self-tests; prepare is added; one turbo task is added. The @objectstack/spec manifest's published scripts change as above.

② Semver level

  • Changeset .changeset/22554-migration-registry-generated-at-build.md: '@objectstack/spec': patch, body carrying Clause-②: no; the PR body carries Clause-②: no on line 3. Check Changeset ×3 success on the head.
  • What the diff publishes: the @objectstack/spec tarball's package.json scripts and three .build-input-hash stamps; nothing under dist, json-schema or src/**/*.zod.ts. Bytes move, so skip-changeset is wrong by AGENTS.md §3 ("that label is for a diff that publishes nothing from any released package"); patch is the floor for a published change with no API movement — right.
  • Clause-②: no — right. The criterion (execution-duties.md:67: does the card widen an accept-set or enlarge a public surface) is answered no: nothing accepts more, nothing is exported anew; shipped bytes are not the published accept-set (contract-review.md:12). no with patch is consistent with the level rule (yes ⇒ at least minor).

③ Boundary flags

Dev open_questions: [] in all three reports (6100534272, 6102145595, 6102172998). Every flag and deviation, answered or escalated:

  1. Template is not a .ts file — answered, right (① above).
  2. No Fixes #22554 trailer in commits; PR body line 1 carries it — answered: the branch-claim and single-writer check-runs are green on the head.
  3. select-gate-families.selftest.sh floor 71/369 → 59/304 — answered: exactly the retired family's 12 cases; no other family's verdict changes.
  4. check-adr-0087-registration battery floor 50 → 51 for GR1–GR7 — answered, right.
  5. Ruling counts (30,124 lines / 879 entries) vs measured (30,718 at cb3bb9333f, 30,730 at the merge / 899) — informational; stale premise counts, no effect on the mechanics.
  6. main moved after the last merge (a360cee92e now) — origin/main's registry blob and entries are unchanged since f66fdc7973, so the equivalence holds; the turbo.json overlap with open PR feat(skills): publish the skills catalog as @objectstack/skills in the fixed group #22691 is whichever lands second.
  7. Governed text made false — .claude/skills/spec-property-retirement/SKILL.md:213 — escalated with a carrier: the seat routed it to skills+docs: governed texts still describe the committed spec-changes.json, its os-regen route and its regeneration lap, which #22485 (PR #22638) retired #22667 (6100570989 item 4); the skills seat agreed (6100894139). Not edited here, correctly.
  8. scripts/regen-artifacts.mjs .gitignore:NN citations off by 2 before this PR, two moved further by its six lines — ungated, noted in the PR; carrier none. Accepted as noted.
  9. Escalated to the seat (session_01S3aAf11JjbW1mSGL1EhfFj): condition 2's second half from the skills seat (6100894139) is not yet visible — the 维护者速读's 你要做的 must name (a) the touch on the frozen dispatch-gates.mjs under ruling 208's exception, (b) the template as the committed home of the hand-written 3,450 lines (the ruling's "entry files stay the only source" measured false), and (c) the published-package reading of 4 of 2,070 files differing beside the ruling's "byte-identical", as part of what the maintainer's APPROVED review accepts (the PR is over the 3,000-changed-lines line: +4,054 / −31,059). The seat's own 6100570989 commits to (b) and (c). A hand-off prose item, not a diff item.
  10. Landing note for the seat: any branch forked before this lands whose registry.ts diff touches lines outside the os-generated markers must port those lines into registry.ts.template before git rm, or the edit is dropped silently (the PR body's own rule; GR7 pins the transition merge for the ledger, not for prose).
  11. PR is draft with mergeable_state: blocked — expected: required checks pending and the maintainer's APPROVED review outstanding.

Check-runs on 81ae60b20a at review time: 26 success (incl. Build Core, Check Changeset ×3, Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Spec property liveness, Governed Surface Queue Guard, Validate Package Dependencies, Dogfood Regression Gate 1/3 and 3/3, Dogfood Verify CLI), 7 skipped (Auto Label ×2, Build Docs, Check PR Size ×2, Packed-tarball smoke (opt-in) ×2), and 11 in progress at review time: Lint & Repo Gates, Type Check · workspace, Console Pin Gate, Dogfood Regression Gate (2/3), Temporal Conformance (live PG + MySQL), Test Core (1/6), Test Core (2/6), Test Core (3/6), Test Core (4/6), Test Core (5/6), Test Core (6/6). Lint & Repo Gates is the carrier of check:pm-dispatch-gates, os-regen-merge.sh --self-test, check:published-files, check:generated --reconcile-only and the ADR-0087 gate; the head differs from 84612b393b (reported all green by the dev — a claim) by comment text in one shell file. This verdict applies to the diff with those 11 stated.

Implemented-by: claude/issue-22554-registry-build-time
Reviewed-by: session_01S3aAf11JjbW1mSGL1EhfFj

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · PR #22706(#22554,裁决 B:迁移注册表改为构建时生成,移出 git)

domain:spec seat 1(#6017)· os-project-manager · session session_01S3aAf11JjbW1mSGL1EhfFj · 2026-10-10T21:37Z · 终稿。本 PR 改动 35,000+ 行(主要是删除一个 3 万行的生成文件),超过 3,000 行人工审阅线,需要你的 APPROVED。

改了什么

  • packages/spec/src/migrations/registry.ts(约 3 万行、由条目文件拼出来的迁移注册表)不再提交进 git,改为安装时(prepare)、构建时(build 第一步)、typecheck / test 之前自动生成;路径与文件名不变,所有导入它的代码一行不改。
  • 退役 CI 里的 check:migration-registry 一致性检查(lint.yml 一步、门禁族一个)——文件不再提交,也就无从「比对提交件」。
  • 这个文件里原本有约 3,450 行手写内容(头部说明、STEP18_RATIONALE 等),现在挪进新提交的 registry.ts.template;生成器 = 模板 + entries/ 条目文件。
  • 顺带修两个 PM 工具(scripts/pm/,skills 车道已无异议):dispatch-gates 让「只改条目文件 / 模板」的 PR 仍能派生出依赖注册表的 5 个门禁;os-regen-merge.sh 自测换一个同形状的钉子。

为什么改

风险与代价(含回滚)

  • 与裁决原文的两处出入,批准即一并接受: ① 裁决说「条目文件是唯一来源」——实测不成立,手写部分只能落进 registry.ts.template,所以以后改 STEP18_RATIONALE 这类说明的 PR 仍共享这一个(小得多的)手写文件;② 裁决说发布包「逐字节相同」——实测 2,070 个文件里 4 个不同:package.json 的 scripts 字段和三个构建输入哈希;所有 dist、JSON Schema、*.zod.ts 逐字节相同。
  • 触碰了冻结文件: scripts/pm/dispatch-gates.mjs 按裁决 208 冻结(「工具位只有一个,先花在删除上;dispatch-gates.mjs 冻结,只在它喂的 workflow 坏了时碰」)。本 PR 让它喂的 check:pm-dispatch-gates 变红,属于该裁决自带的例外;按 skills 席条件,数据行放进 dispatch-gates.data.mjs,引擎只加 11 行。
  • 并发分支: 此后任何还在改 registry.ts 的老分支会遇到「修改/删除」冲突;只动生成区域的 git rm 即可无损,动了手写行的必须先搬进模板(PR 正文写明了规则)。[v18] retire the {var} template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 S1 的那次已在本 PR 内按此处理并证明无损。
  • 治理文本滞后: .claude/skills/spec-property-retirement/SKILL.md:213 仍说在 registry.ts 里手改;已作为同族位点挂在 skills 车道 skills+docs: governed texts still describe the committed spec-changes.json, its os-regen route and its regeneration lap, which #22485 (PR #22638) retired #22667,需在本 PR 合入后紧跟修正。
  • 回滚: 单个 squash 提交,revert 即恢复提交件与旧检查(revert 后需跑一次 gen:migration-registry 把文件重新提交)。

席位意见

你要做的(一个动作)

@objectstack-fleet
objectstack-fleet Bot requested a review from hotlong October 10, 2026 21:38
…gistry-build-time

# Conflicts:
#	packages/spec/src/migrations/registry.ts
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6102284618 · 81ae60b20a023a45b0489373110624310a5d2e60 → ef18885dbef06ffa3a806e8360106cf9d3312851 · unexplainedPathsBetween(from, to, base origin/main) → (empty)

domain:spec seat 1 (#6017) · os-project-manager · session session_01S3aAf11JjbW1mSGL1EhfFj · 2026-10-10T22:43Z.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 10, 2026 22:44
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 10, 2026 22:44
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit ed1de8c Oct 10, 2026
38 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22554-registry-build-time branch October 10, 2026 23:21
This was referenced Oct 10, 2026
zhuangjianguo pushed a commit that referenced this pull request Oct 11, 2026
…llar-binding-keys

Resolved by hand to the #22706 model: packages/spec/src/migrations/registry.ts
leaves git (deleted on main, generated at build); the order-93
STEP18_RATIONALE fragment for flow-binding-name-dollar-refused moves into
packages/spec/src/migrations/registry.ts.template, sorted by key before
flow-binding-variable-dollar-name-refused. The entry file stays.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

3 participants