feat(spec)!: retire the connector triggers array — the ConnectorTrigger shape nothing registered, polled or received (#20287) - #20587
Conversation
…s, pins Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…d surface baselines Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
… the triggers retirement Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…nnector-triggers-retired
…ow rule that landed on main Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…nnector-triggers-retired Resolves packages/spec/src/migrations/registry.ts: main's side (step 18's rationale as key-sorted fragments, conversionIds derived), with this branch's hand-written rationale carried over as fragment edits — the connector pair note in duration-keys-unit-in-key, the resilience fragment's tail, and a new connector-triggers-retired fragment (order 47). Generated regions are main's here and are regenerated in the next commit. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…ter merging main Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
… pending resilience note's 'rename is unaffected' sentence Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 649df038ed80ceb4ebdfbad74882d4372ba1bff1 && git checkout 649df038ed80ceb4ebdfbad74882d4372ba1bff1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 4f8c62b3975236c86e079f64bff19dc1005b50f6 && git checkout -B drift-repro 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 && git merge --no-ff 4f8c62b3975236c86e079f64bff19dc1005b50f6
node scripts/docs-audit/affected-docs.mjs --json 1322cc72c96f9e80240c1fe0a7d12708f3b269b9
|
Contract reviewServed-tier: Director seat (objectstack#12708, ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…nnector-triggers-retired Resolves packages/spec/liveness/README.md: main's realtime_subscription and analytics_cube rows, this branch's connector row (the only row either side changed in that hunk). step 18's rationale auto-merged beside main's cube-member-inner-name-retired fragment (order 47); this branch's connector-triggers-retired fragment moves to order 48, one past the highest now present. Generated regions are regenerated in a following commit. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…0 label, and name the released resilience note the changeset corrects main now carries the 17.5.0 label (the version-packages merge), and 17.5.0 still accepts `triggers`, so it is the last release the retirement follows. The 20273 note was consumed into the 17.5.0 changelog entry; the correction sentence names it by that release and its D2 id. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
|
Director note · record superseded by the head move · 2026-09-29T07:54Z Director seat (objectstack#12708,
Before the landing: the fix pushed, CI green, and a delta record at that head (the delta from |
…e live registry, not a snapshot of its stamps Three pins hard-coded which retirements a floor opens — 'every retirement is stamped 17.4.0 or earlier' — and went red when connector-triggers-removed, landing after the 17.5.0 version pass, was stamped 17.5.0 as the census rule requires. The door behaves as its rule says; the pins now derive the opened set from ALL_CONVERSIONS, keep every refusal assertion, and keep the 'authored-current' verdict at full strength on a floor past every stamp. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Director seat (objectstack#12708, ① Derived judgments
② Semver levelUnchanged: ③ Boundary flags
Implemented-by: VERDICT: PASS |
Contract reviewServed-tier: Inputs read: card #20287 (body and all 11 comments, the ruling ① Derived judgmentsAccept-set and public-surface changes the diff implies
Text an author acts on — sentence by sentence
② Semver level
③ Boundary flagsDev flags (stage-2 report
Deviations this record names that the dev did not list as such:
Check-runs on Verdict reasoning: the tree, the accept-set, the stamp, the ledger and the pins are right; the two failing items are two sentences in the changeset — text that ships to consumers as Implemented-by: VERDICT: FAIL Adopted and posted by Generated by Claude Code |
…eleased 17.5.0 entry that is true of 17.5.0 Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…op 'never released' Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…me id retired, 17.4.0 and 17.5.0 Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
|
Director note · this seat's delta PASS Director seat (objectstack#12708, The rest of |
Contract reviewServed-tier: Inputs read: card #20287 (body and all 13 comments — the maintainer's ruling ① Derived judgmentsAccept-set and public-surface changes the diff implies
Text an author acts on — sentence by sentence
② Semver level
③ Boundary flagsDev flags (stage-2 report
Deviations this record names that the dev did not list as such:
Check-runs on Verdict reasoning: the accept-set change is the one the ruling ordered and is delivered on both carriers with the ADR-0087 kit complete; the stamp is right on both rules and confirmed against the published tarball; the ledger, the baselines and the pins are right; the two changeset sentences that failed the prior head are gone and every sentence an author acts on is now true and sourced; every dev flag is answered or carried by a ruling on the card; the head is green. Implemented-by: VERDICT: PASS Adopted and posted by Generated by Claude Code |
…/src remainder to the commits and ADRs that decided them (stage 6) (objectstack-ai#20606) Part of objectstack-ai#20234 Clause-②: no Stage 6 of the staged sweep: the `packages/spec/src` remainder outside `migrations/` and the held files. Its claim is `5884233505`, with 22 files named there. Every comment or docblock line in those files that cited a tracker number answering 404 now cites what decided its rule, in ruling C+D's form C. That is the commit on `main` that decided the rule, or, for one number, the ADR amendment that records the ruling. Each line says in its own words what was decided. Comments only: 66 lines out, 66 in, across 21 files. No code token, string literal, `describe()` text or message-catalog string moves. Two dead sites stay byte-identical, because a test reads each one by literal. The census is the gate's own `node scripts/check-issue-citations.mjs --census --json`, filtered to the 22 paths. Before: base `c876a7426d`, board enumerated (185 pages, frontier objectstack-ai#20590). After: head `9d63cb6548`, frontier objectstack-ai#20604. ## Measurement | file (under `packages/spec/src/`) | dead before | after | numbers, then anchor | |---|---:|---:|---| | `api/rest-server.zod.ts` | 11 | 0 | objectstack-ai#14691 ×9 to `b3a63d32c`; objectstack-ai#14369 ×2 to `a3d5724c8` | | `system/i18n-resolver.ts` | 14 | 0 | objectstack-ai#12961 ×6 to `901355c3b`; objectstack-ai#13218 ×4 to `c45d8e6b4`; objectstack-ai#8460 ×2 to ADR-0029 D9.2a; objectstack-ai#10926 to `d173125fb`; objectstack-ai#13109 to `8b236c826` | | `system/operation-message.ts` | 4 | 0 | objectstack-ai#12493 ×4 to `aa5994e17` (docblock lines only) | | `system/translation.zod.ts` | 2 | 0 | objectstack-ai#10926 ×2 to `d173125fb` | | `system/core-services.zod.ts` | 1 | 0 | objectstack-ai#6604 to `d127ff002` | | `system/dev-login.zod.ts` | 1 | 0 | objectstack-ai#17081 to `24d622b94` (objectstack-ai#17556 stays, 200) | | `system/environment-artifact.zod.ts` | 1 | 0 | objectstack-ai#11333 to `e58ea8b38` (objectstack-ai#14865 and objectstack-ai#13457 stay, 200) | | `shared/identifiers.zod.ts` | 7 | 0 | objectstack-ai#12245 ×2 to `c41b42e8d`; objectstack-ai#12144 ×2 to `3a04b0125`; objectstack-ai#12194 and objectstack-ai#12176 (:140-141) to `311433f6b`; objectstack-ai#12176 (:189) to `7986d973f` | | `shared/metadata-collection.zod.ts` | 1 | 0 | objectstack-ai#10485 to `35ad101bc` | | `index.ts` (package root) | 6 | 0 | objectstack-ai#11350 ×5 to `ece4dad31` (objectstack-ai#11709 stays, 200); objectstack-ai#10485 to `35ad101bc` | | `automation/control-flow.zod.ts` | 1 | 0 | objectstack-ai#14419 to `c5a7448d5` (objectstack-ai#14954 stays, 200) | | `automation/execution.zod.ts` | 1 | 0 | objectstack-ai#13681 to `18d816a50` | | `automation/index.ts` | 1 | 0 | objectstack-ai#16659 to `ecdfc9411` | | `automation/schedule-organization.zod.ts` | 1 | 0 | objectstack-ai#16659 to `ecdfc9411` | | `ai/index.ts` | 1 | 0 | objectstack-ai#11350 to `ece4dad31` | | `identity/identity.zod.ts` | 1 | **1** | objectstack-ai#8715 kept at :230 (a test reads it); `2c86fe3ea` added on :231 | | `security/explain.zod.ts` | 1 | 0 | objectstack-ai#8714 to `42b05af89` | | `security/public-form.ts` | 1 | 0 | objectstack-ai#6640 to `2ab1257c9` | | `data/api-derivation.ts` | 1 | **1** | objectstack-ai#6259 kept at :163 (two tests read it); `6968885ef` already on :164; file untouched | | `data/driver/turso.zod.ts` | 2 | 0 | objectstack-ai#6345 ×2 to `e2798fab7` | | `conversions/walk.ts` | 1 | 0 | objectstack-ai#13031 to `b799ac553` | | `meta-spelling/metadata-url-spelling.ts` | 2 | 0 | objectstack-ai#10485 ×2 to `35ad101bc` | | **22 files** | **62** | **2** | 26 numbers, 24 removed: 24 distinct shas and 1 ADR | Per-file counts at base equal the claim's (census `5884031174` at `f11b5f20a2`) in all 22 files. A second instrument agrees site for site: every `#N` in the 22 files, classified by the TypeScript parser, and each of 201 distinct numbers probed by REST `issues/N` without redirects. It found 484 sites, all in comments and none in a string, 26 dead numbers and 62 dead sites. Its string-class positive control found 19 string sites in `api/rest-server.test.ts`. Head: 424 sites and 177 numbers, 175 answer 200 (the same 175), and 2 answer 404 (the two kept sites). Lit controls objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 at every checkpoint (4 at base, 3 at head); dead controls objectstack-ai#16714, objectstack-ai#16715 and objectstack-ai#16697 answered 404 at every checkpoint. ## Why each anchor decides its line Each sha resolves uniquely, is an ancestor of `origin/main` (and of the base), has one parent, and names the number it replaces in its own message or diff. Each was read for the rule its line states. - **objectstack-ai#14691 to `b3a63d32c`**: the retirement of the ten inert `RestServerConfig` keys under ADR-0049 enforce-or-remove. Its own `rest-server.zod.ts` diff wrote all nine `objectstack-ai#14691` lines: the tombstones, the dropped `CrudEndpointPatternSchema` and the `routes` block. - **objectstack-ai#14369 to `a3d5724c8`**: seeded the four `RestServerConfig` liveness ledgers "from the census filed with objectstack-ai#14369" (its changeset heading names the number). It records both facts the two lines state: every CRUD route is mounted from hard-coded method/path pairs, and `routes` is parsed, defaulted and normalized, then never read. - **objectstack-ai#12961 to `901355c3b`**: "Ruled 2026-08-29 (option A)". `translatePage` descends into declared `properties.children`; on an id collision a region-level component wins outright, and among nested matches document order decides. Its diff wrote the `objectstack-ai#12961` lines being replaced. - **objectstack-ai#13218 to `c45d8e6b4`**: exports `walkAddressedPageComponents` and consumes it from both sides; its changeset reads "(objectstack-ai#13218, ruled 2026-08-30)". - **objectstack-ai#13109 to `8b236c826`**: its changeset says the extractor OMITTED keys the resolver reads, and "This matches the second half". The line now says the second half went live and this commit repaired it. - **objectstack-ai#8460 to ADR-0029 D9.2a**: ruling C's first rung. The amendment "D9.2a — AMENDMENT (2026-08-13)" records the option-A ruling: an extender's scalar applies only while the fold's base still carries the packaged owner's value. It also records that the mechanism is deliberately the same comparison-based one the catalog uses one layer up. The heading marker `[objectstack-ai#8460]` becomes `[ADR-0029 D9.2a]`. - **objectstack-ai#10926 to `d173125fb`**: "Option A per the maintainer ruling on objectstack-ai#10926 (2026-08-22): drop the key", the `submitLabel` retirement all three lines describe. - **objectstack-ai#12493 to `aa5994e17`**: adds `record_write_denied` and `approval_recall_not_submitter` ahead of their emitters, with no placeholders. Its diff wrote the four docblock lines. The catalog's rendered strings are untouched, per hypothesis 5. - **objectstack-ai#6604 to `d127ff002`**: "Per the maintainer's 2026-08-08 Option-B ruling the kernel side takes the domain-specific name", matching `KernelServiceMapSchema`. - **objectstack-ai#17081 to `24d622b94`**: lands objectstack-ai#17556 as "Suggestion 1 of objectstack-ai#17081". The line keeps objectstack-ai#17556 and names the parent card in words. - **objectstack-ai#11333 to `e58ea8b38`**: declares `grantedPermissions`, described by the commit itself as "the artifact-contract half of objectstack-ai#11333 option A / the objectstack-ai#13457 batch ruling". The line keeps objectstack-ai#14865 and objectstack-ai#13457 and states the ruled option in words. - **objectstack-ai#12245 to `c41b42e8d`**: rewrote this docblock from "the per-surface census (its os-dev-report comment, measured on origin/main @ e2debee)" and carries the 1218-values measurement. The report comment lived on the deleted card, so the commit is now the record, and the line says so. - **objectstack-ai#12144 to `3a04b0125`**: wrote the storage-owned length-ceiling note and its storage-column pin; its changeset heads "(objectstack-ai#12144)". - **objectstack-ai#12194 / objectstack-ai#12176 to `311433f6b`** (:140-141): stage 1, the item-name grammar declared and refused at the publish door; its message reads "Stage 1 of objectstack-ai#12176". **objectstack-ai#12176 to `7986d973f`** (:189): "Retire compound-name metadata addressing", stage 3 of the maintainer-ruled retirement. - **objectstack-ai#10485 to `35ad101bc`**: retires the `themes` carrier, `ThemeSchema` and the `PLURAL_TO_SINGULAR` fold ("Ruled B"). Its own diff wrote all four lines. - **objectstack-ai#11350 to `ece4dad31`**: "Invariant recorded (maintainer ruling 2026-08-23)". It also points the premise-delta note at objectstack-ai#11709, which is what `index.ts:148` now says. This is stage 1's wording for `kernel/index.ts:53`. - **objectstack-ai#14419 to `c5a7448d5`**: `create_record` surfaces the engine's `DUPLICATE_RECORD` code and the engine binds it on `$error`, the founding case the line names. Its message names objectstack-ai#14419 as the card it lands. - **objectstack-ai#13681 to `18d816a50`**: declares the run-level `FlowRunSummary.failed`, the spec half of the contained-failure contract. - **objectstack-ai#16659 to `ecdfc9411`**: declares the start-node `config.organization` key and "the one refusal sentence every enforcement point says". Its sub-commits pin "the three objectstack-ai#16659 consequences", so it is also the commit that closed the defect the second line describes. - **objectstack-ai#8714 to `42b05af89`**: "ONE closed contributor-state enumeration", maintainer-ruled 2026-08-18. - **objectstack-ai#6640 to `2ab1257c9`**: `preserveAudit` is UPDATE-only (stage 1's anchor for the same rule). - **objectstack-ai#6345 to `e2798fab7`**: its own `turso.zod.ts` diff wrote both lines ("The maintainer's objectstack-ai#6345 ruling closes it…", "(objectstack-ai#6345 fork 2)"). The wording is stage 3's in `config-registry.zod.ts`. - **objectstack-ai#13031 to `b799ac553`**: adds `mapViewPayloads` to `walk.ts`, the centralized walk the heading describes. Its message names objectstack-ai#13031 as the card it lands. - **objectstack-ai#8715, kept**: `2c86fe3ea` ("Maintainer ruling 2026-08-15 (disposition B: delete)"; its diff wrote :230) now sits on :231. ## Mechanical proof - **Token guard** (my `tokcmp.mjs`: TypeScript 6.0.3 leaf tokens, JSDoc kinds excluded, controls mutate the head text in memory only). Base `c876a7426d` against the head, 21 files, 37,539 base tokens: - Real run: 0 files with a token change (exit 0). - Comment-insertion control (`ai/index.ts`): 0 (exit 0). - Code-insertion positive control (`system/i18n-resolver.ts`): DIFFER at token 14452 (exit 1). - String positive control (a real `StringLiteral` in `system/operation-message.ts`, found by the parser): DIFFER at token 5 (exit 1). - The first string-control attempt matched a quoted fragment inside a comment and did not fire. It was a vacuous control, not a measurement, and the parser-located control above replaced it. - **Line balance**: every file is +N/−N (66/66 across 21 files); every line count is equal at base and head. - **Tracker numbers**: added-not-removed is empty in every file, and no `PR #N` is on an added line. Net-removed: 60 sites, 24 numbers. - **Shas**: 24 distinct on added lines, 0 on removed lines. - `rev-parse --disambiguate` answers 1 object for each. - `merge-base --is-ancestor` exits 0 against `origin/main` `e666636fd9` and against the base. - Each is single-parent; the repository is not shallow; the control leg `e9584681a4` exits 0. - Each commit's own message (17 of 24) or diff (all 24) names the number it replaces. - **Literal readers**: every string literal in the repository that carries one of the 26 numbers was matched against the 22 files' text. Three hits read these files: - `data/api-derivation.test.ts:236` splits on `[objectstack-ai#6259]`; - `packages/runtime/src/api-exposure.test.ts:152` splits on `objectstack-ai#6259`; - `identity/api-key-retirement.test.ts:118` asserts `are NOT declared here (objectstack-ai#8715`. - Those lines are the two kept sites. No test or script matches any rewritten line by pattern. ## Tests and gates (at head `9d63cb6548`) - `pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*` under `os-verify-lock`: Tasks 71 successful, 71 total, VERDICT command-exit 0. - `pnpm --filter @objectstack/spec check:generated`: exit 1, `check:docs` stale (1 of 15). `check:generated --fix` then regenerated exactly that artifact: 2 pages, 3 lines, each its docblock line verbatim. - `content/docs/references/automation/schedule-organization.mdx` - `content/docs/references/data/driver-turso.mdx` - The re-check in the gate run below is exit 0. - `vitest run --maxWorkers=2` over the touched areas (`src/api/rest-server.test.ts`, `src/api/rest-api-config-dead-keys-retirement.test.ts`, `src/system`, `src/shared`, `src/automation`, `src/ai`, `src/identity`, `src/security`, `src/data/driver`, `src/conversions`, `src/meta-spelling`): Test Files 159 passed (159), Tests 5163 passed (5163). - The 23 spec suites outside those areas that read a touched file's source text or name it: Test Files 23 passed (23), Tests 540 passed (540). - `scripts/{tombstoned-row-status,strictness-ledger,file-description,skill-map-guards,root-index,export-origins,category-title,split-entries,dist-freshness,dist-freshness-adoption,root-entry-type-nameability.pin}` tests; - `src/type-alias-convention.pin`, `src/contracts/{automation-result-status.pin,automation-service,scoped-context}`, `src/api/{export-job-family-retirement,api-entry-graph.pin}`, `src/eager-entry-import`, `src/integration/connector-author-shape`, `src/ui/{interaction-config-retirement,notification,strictness-batch14}`, `src/migrations/migrations`. - `scripts/build-schemas-check-mode.test.ts` is left to CI: it imports rather than reads, and rebuilds schemas in a temp tree. - `pnpm --filter @objectstack/spec typecheck`: exit 0; `check:test-typecheck` OK (53 files / 251 errors / 138 pinned signatures held). - Lint, a proven narrowing: `eslint --no-inline-config --format json` over the 21 touched `.ts` files gives 21 files, 0 errors, 0 warnings. - `isPathIgnored` is false for all 21, read through eslint's API. - `eslint.config.mjs:327-328` says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. - The repo-wide `pnpm lint` is CI's. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`: 108 families derived and run, every one exit 0. `--ran` reads "108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN". Among them: - `pnpm check:issue-citations` plus the live diff-scoped `node scripts/check-issue-citations.mjs` judged 7 citations across 21 files, 7 resolve: the live numbers kept beside the anchors (objectstack-ai#9249, objectstack-ai#14954, objectstack-ai#17556, objectstack-ai#14865, objectstack-ai#13457, and objectstack-ai#11709 twice). - `pnpm check:doc-authoring`: 16,765 customer-facing strings across 1,175 spec sources clean; the sibling baseline holds. - Changeset: `patch` for `@objectstack/spec`. 13 of the 21 touched sources are `src/**/*.zod.ts`, which `files[]` ships verbatim, and the rewritten docblocks reach `dist`. For example, `ruled collision arbitration (commit 901355c)` is in 1 `.d.ts`, and the unchanged neighbouring sentence in the same exported docblock (the positive control) is in 1 `.d.ts`. - Merge probe: a no-driver `merge-tree` of the head onto `origin/main` `7a1faf1a5d`, from a bare shared clone, exits 0. The 3 commits `main` gained since the base touch none of this diff's files. No merge was made, as stages 1–4 did. - No ablation or reverse verification: the change is comment-only, so there is no behaviour to invert. ## Hypotheses (measured first) 1. **Holds.** The population is exactly the claim's 22 files: 62 dead sites at the tip, equal per file to the census at `f11b5f20a2`. 2. **Holds, with nothing to respell.** No sibling-qualified pair occurs among the 62 sites; no `pre-#N` spelling is dead here. 3. **Holds.** Re-read at 2026-09-29T06:23Z, after the last push and before this PR was opened: all 14 open PRs' full file lists (1,116 files in the version PR alone), and the newest `Claim:` on all 15 `pm:dispatched` cards. None names any of this PR's 24 paths. The five exclusions stay excluded. 4. **Holds.** The two projected pages were regenerated by the generator, never by hand. No other page under `content/docs/references/` carries any of the 26 numbers. 5. **Holds.** No `#N` in the 22 files is inside a string; the two literal-read sites stay as tokens. In `system/i18n-resolver.ts` and `system/operation-message.ts` only docblock and line-comment lines moved. ## Deviations - The file surface is 21 of the 22 named files. `data/api-derivation.ts` is untouched, because its one dead site is read by literal and its commit already stands on the next line (stage 3's disposition). - Six changed lines held no dead number. Each is the other half of a rewritten sentence: `rest-server.zod.ts:756`, `identifiers.zod.ts:19`, `index.ts:133`, `environment-artifact.zod.ts:137`, `schedule-organization.zod.ts:82`, and `identity.zod.ts:231` (the commit placed beside the kept :230). - Commit trailers follow AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`); the pre-push trailer check passed on every push. ## Acceptance notes **What stays for later stages.** At the tip `7a1faf1a5d` with this PR applied, `packages/spec/src` holds **260** dead sites (34 numbers). This is the gate's census on this head, with the four spec sources `main` changed since the base re-extracted and re-probed at the tip. By area: - `migrations/` **233**: objectstack-ai#20233 edits the same entry files; the author-shown fields are its form D. - `conversions/registry.ts` **12**: held by PRs objectstack-ai#20570 and objectstack-ai#20458. - `stack.zod.ts` **9**: free now; PR objectstack-ai#20579 landed as `7a1faf1a5d` at 06:02Z, after the claim, so it stayed excluded here. - `data/analytics.zod.ts` **3**: PR objectstack-ai#20458. - `integration/connector.zod.ts` **1**: objectstack-ai#20287, PR objectstack-ai#20587. - `data/api-derivation.ts:163` (objectstack-ai#6259) and `identity/identity.zod.ts:230` (objectstack-ai#8715), **1** each: kept because `api-derivation.test.ts:236`, `packages/runtime/src/api-exposure.test.ts:152` and `api-key-retirement.test.ts:118` read them by literal. Removing them is a test-string change, form D, outside this card's comment-only scope. **Carried from earlier stages, outside the gate's census** (which blanks strings and defers test files): the dead-number test-title strings, the two `why` strings, the `PROVENANCE_WAIVERS` reason, the two `AGGREGATION_CASES` notes, and the `liveness/**` notes. **Outside `packages/spec/src`** (objectstack-ai#20556's lane): `packages/spec/scripts/check-entry-nameability.ts` cites objectstack-ai#11350 in its header (:14) and PRINTS "recorded on objectstack-ai#11350" in its failure text (:727); `packages/spec/scripts/root-entry-type-nameability.pin.test.ts` cites it too. Commit `ece4dad31` is the anchor, already verified here. **Rung.** Five of the anchored retirements also have ADR-0087 D3/D2 entries: `identity-api-key-schema-retired`, `metadata-item-name-grammar-enforced`, `rest-server-config-dead-keys-retired`, `stack-themes-carrier-retired` and `translation-component-submit-label-retired`. This PR takes the commit rung, as stages 1–5 did. The D3 id is the more durable in-repo record if the ruling's first rung is later read to include those entries. **Wording, each true of its commit.** - `dev-login.zod.ts:10` names objectstack-ai#17081 in words ("suggestion 1 of its parent card"). - `environment-artifact.zod.ts:136-137` states objectstack-ai#11333's option A as "the option the objectstack-ai#13457 batch ruling chose". - `identifiers.zod.ts:18` drops "its `os-dev-report` comment is the measurement of record", since that comment went with the card, and names the commit as the record. **Observation, not filed** (a pre-existing live citation, not a tracker number; carrier: none): `environment-artifact.zod.ts:137` and `packages/runtime/src/security/artifact-granted-permissions.ts:6` cite "ADR-0025 §3.5 step 2" for the granted set. At the tip, §3.5's numbered step 2 is "Compatibility" and "Permission consent" is step 3. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…data/analytics.zod.ts to the commits that decided them (stage 7) (objectstack-ai#20616) Part of objectstack-ai#20234 Clause-②: no Stage 7 of the staged sweep: `packages/spec/src/stack.zod.ts` and `packages/spec/src/data/analytics.zod.ts`, both freed by landings (PR objectstack-ai#20579 and PR objectstack-ai#20458). Its claim is `5885635758`. Every comment or docblock line in those two files that cited a tracker number answering 404 now cites the commit on `main` that decided its rule, in ruling C+D's form C, and says in its own words what was decided. Comments only: 12 lines out, 12 in, across 2 files. No code token, string literal or `describe()` text moves. No dead site stays: none of the 12 is read by literal. The census is the gate's own `node scripts/check-issue-citations.mjs --census --json`, filtered to the two paths. Before: base `0f6dcac5e9`, board enumerated (185 pages, frontier objectstack-ai#20611). After: head `cc0580d404`, board enumerated (185 pages, frontier objectstack-ai#20615). ## Measurement | file (under `packages/spec/src/`) | dead before | after | numbers, then anchor | |---|---:|---:|---| | `stack.zod.ts` | 9 | 0 | objectstack-ai#10485 ×2 (`:415`, `:1023`) to `35ad101bc`; objectstack-ai#6238 (`:633`) to `c8d6f6e08`; objectstack-ai#14192 (`:1233`) to `4d0d9445a`; objectstack-ai#14686 ×2 (`:3037`, `:3194`) to `279431e7a`; objectstack-ai#14662 ×3 (`:4510`, `:5070`, `:5293`) to `35dffeace` | | `data/analytics.zod.ts` | 3 | 0 | objectstack-ai#10194 ×3 (`:404`, `:407`, `:485`) to `2306a765c` | | **2 files** | **12** | **0** | 6 numbers removed, 6 distinct shas | Per-file counts at base equal the claim's (9 and 3, from stage 6's census). A second instrument agrees site for site: every `#N` in the two files, classified by the TypeScript parser, and each of the 84 distinct numbers of 100 or more probed by REST `issues/N` without following redirects (the other 3 are the ordinals `Prime Directive objectstack-ai#12`, `batch objectstack-ai#23`, `batch objectstack-ai#57`). - Base: 244 sites, all in comments (0 strings, 0 code). 78 numbers answer 200 and 6 answer 404: the same 6 numbers and the same 12 sites as the gate. - Its string-class positive control found 11 string sites in `kernel/manifest-unknown-keys.test.ts` and `packages/cli/src/utils/lower-callables.test.ts`. - Head: 232 sites, 78 numbers, all 78 answer 200 (the same 78), none answers 404. - Lit controls objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 at every checkpoint (3 at base, 3 at head); dead controls objectstack-ai#16714, objectstack-ai#16715 and objectstack-ai#16697 answered 404 at every checkpoint. ## Why each anchor decides its line Each sha resolves uniquely, is an ancestor of `origin/main` (and of the base), and has one parent. No file under `docs/adr/**`, `docs/NORTH-STAR.md` or `scripts/adr-anchors/` names any of the six numbers or records these rules, so each takes the commit rung, as stages 1–6 did. - **objectstack-ai#10485 to `35ad101bc`** (`:415`, `:1023`): retires the `themes` carrier key and `ThemeSchema` under ADR-0049. Its message records the ruling, "Ruled B (退役授权面, 2026-08-21)", and its own `stack.zod.ts` diff wrote both lines. `:415` keeps ADR-0049 and the ruling in its words; the D3 entry `stack-themes-carrier-retired` it names on `:423` is unchanged. This is the anchor stages 1, 5 and 6 used for the same retirement. - **objectstack-ai#6238 to `c8d6f6e08`** (`:633`): widens the array member of `functions` so its `handler` also takes the lowered string ref, which is the fix for `objectstack build` refusing its own array output. Its message names objectstack-ai#6238, and its own diff wrote the line. objectstack-ai#4343 and objectstack-ai#4976 on the same line stay (both 200). - **objectstack-ai#14192 to `4d0d9445a`** (`:1233`): turns `ManifestSchema` and its nested blocks into `strictObject` and flips the assembled-body strip pin to a refusal pin; each of its sub-commits names objectstack-ai#14192. The line itself was written later by `c78c9180de`, whose own message says "objectstack-ai#14192 closed ManifestSchema with strictObject", so the commit that closed it is the anchor. - **objectstack-ai#14686 to `279431e7a`** (`:3037`, `:3194`): "defineStack refuses two actions that resolve to one scope-qualified runtime key". Its subject names objectstack-ai#14686, and its diff adds `collectDuplicateActionKeyErrors` and the changeset for that refusal. Both lines were written later by `773a99960a` (PR objectstack-ai#15022), whose message describes the same "same-key rule, which runs before the merge". - **objectstack-ai#14662 to `35dffeace`** (`:4510`, `:5070`, `:5293`): "composeStacks refuses two stacks whose actions resolve to one scope-qualified runtime key". It checks the composed set with the rule `defineStack` applies within one stack, with no `actionConflict` option (maintainer ruling 2026-09-03). Its message does not name objectstack-ai#14662; its own `stack.zod.ts` diff wrote all three `(objectstack-ai#14662)` lines. - **objectstack-ai#10194 to `2306a765c`** (`analytics.zod.ts:404`, `:407`, `:485`): binds `analytics_cube` (and `theme`) in `UNREGISTERED_KIND_SCHEMAS`, so `PUT /meta/analytics_cube/:name` parses through `CubeSchema`, and gives `CubeSchema` the `...MetadataProtectionFields` spread. Its message names objectstack-ai#10194, and its own diff wrote all three lines. The `[objectstack-ai#10194]` markers become `[commit 2306a76]`, the spelling stages 1 and 5 already use in `kernel/metadata-type-schemas.ts`. ## Mechanical proof - **Token guard** (my `tokcmp.mjs`: TypeScript 6.0.3 leaf tokens, JSDoc kinds excluded, controls mutate the head text in memory only). Base `0f6dcac5e9` against the head, 2 files, 17,249 base tokens: - Real run: 0 files with a token change (exit 0). - Comment-insertion control (`data/analytics.zod.ts`): 0 (exit 0). - Code-insertion positive control (`stack.zod.ts`, a declaration appended): DIFFER at token 15388 (exit 1). - String positive control (the first `StringLiteral` the parser locates in each file): DIFFER at token 5 (exit 1), once per file. - `describe()` positive control (the first `.describe()` string argument the parser locates: `stack.zod.ts:133`, `analytics.zod.ts:244`): DIFFER at tokens 507 and 442 (exit 1). - **Line balance**: `stack.zod.ts` +9/−9, `data/analytics.zod.ts` +3/−3; line counts equal at base and head (5344 and 853). - **Tracker numbers**: added-not-removed is empty in both files, and no `PR #N` is on an added line. Net-removed: 12 sites, 6 numbers. The only numbers on added lines are objectstack-ai#4343 and objectstack-ai#4976, which stay on `:633`. - **Shas**: 6 distinct on added lines, 0 on removed lines. - `rev-parse --disambiguate` answers 1 object for each. - `merge-base --is-ancestor` exits 0 for each, against `origin/main` `7510663c87` and against the base; each is single-parent; the repository is not shallow. - **Literal readers**: all 26 string, template and regex literals in the repository that carry one of the six numbers (42 code files) were matched against the two files' base text: 0 occur there. Each removed line was also cut into 4-word windows (96) and searched across the tree: the 9 hits inside string literals are other files' own test titles sharing a phrase ("the ADR-0010 protection envelope", "an assembled body is"), and none reads either file. The source-text readers of the two files read code, not these comments: `compose-stacks-refusal-envelopes.test.ts` counts `throw new Error(`, and `check-stack-collection-maps.mjs` and `check-skill-top-level-keys.mjs` read the declared collections and keys. ## Tests and gates (at head `cc0580d404`) - `pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*` under `os-verify-lock`: Tasks 71 successful, 71 total, VERDICT command-exit 0. - `pnpm --filter @objectstack/spec check:generated` under the lock: all 15 generated artifacts up to date, `check:docs` over `content/docs/references/**` included; VERDICT command-exit 0. No reference page projects any of the 12 lines, so none is regenerated. - `vitest run --maxWorkers=2` under the lock over the two files' own suites (`src/stack*`, `src/compose-stacks*`, `src/define-stack*`, `src/assembled-package-body`, `src/data/analytics*`, `src/data/cube*`): Test Files 35 passed (35), Tests 976 passed (976). - The 37 spec suites that read source text across `src/`, or carry one of these numbers, under the lock: Test Files 37 passed (37), Tests 759 passed (759). - `scripts/{category-title,dist-freshness,dist-freshness-adoption,file-description,strictness-ledger,strictness-ledger-doc,root-index,skill-map-guards,export-origins,split-entries,root-entry-type-nameability.pin}`, `scripts/liveness/{evidence,tombstoned-row-status}`; - `src/type-alias-convention.pin`, `src/eager-entry-import`, `src/api/{api-entry-graph.pin,auth,export-job-family-retirement}`, `src/ai/tool-confirmation-prescription-tense.pin`, `src/data/{currency-mode-family-closure.pin,external-lookup-retirement}`, `src/identity/position-delegatable-enforcer.pin`, `src/integration/{connector-connection-timeout-retirement,connector-resilience-keys-retirement}`, `src/security/rls-tags-retirement`, `src/shared/{alias-integrity,retired-key-migrate-sentence}`, `src/system/{compliance-families-retirement,constants/platform-object-names,email-template-floor-locale-parity.pin,message-queue-retirement}`, `src/ui/{action-requires-confirmation-docblock.pin,i18n,interaction-config-retirement,strictness-batch14}`, `src/kernel/{manifest-unknown-keys,metadata-type-schemas}`. - Left to CI: `scripts/{build-schemas-check-mode,def-key-collisions,openapi-self-consistency}` (each rebuilds artifacts in a temp tree) and `scripts/{check-generated-ledger,check-generated-fix-rebuild.pin}` (read the ledger and `dist`). None reads comment text. - `pnpm --filter @objectstack/spec typecheck` under the lock: exit 0; `check:test-typecheck` OK (53 files / 251 errors / 138 pinned signatures held). - Lint, a proven narrowing: `eslint --no-inline-config --format json` over the 2 files gives 2 files, 0 errors, 0 warnings. - `isPathIgnored` is false for both, read through eslint's API. - `eslint.config.mjs:327-328` says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. - The repo-wide `pnpm lint` is CI's. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`: 79 families derived and run, every one exit 0. `--ran` reads "79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN". Among them: - `pnpm check:issue-citations` (self-test, 114 cases in 8 batteries) and the live diff-scoped `node scripts/check-issue-citations.mjs`: it judged the 2 citations on added lines, objectstack-ai#4343 and objectstack-ai#4976, and both are live issues. - `pnpm check:doc-authoring`: 16,804 customer-facing strings across 1,179 spec sources clean; the sibling baseline holds. - `pnpm check:stack-collection-maps`: 8 enumerations reconciled against 31 declared collections. - Changeset: `patch` for `@objectstack/spec`. Both files are `src/**/*.zod.ts`, which `files[]` ships verbatim, and the rewritten docblocks reach `dist`: "posture: commit 4d0d944 closed" and "[commit 2306a76] This docblock used to say" are each in 2 `.d.ts`, their old spellings in 0. Positive control: the unchanged neighbouring sentence "BY INHERITANCE — an undeclared key on one is REFUSED" is in the same 2 `.d.ts`. - Merge probe: a no-driver `merge-tree` of the head onto `origin/main` `7510663c87`, from a bare shared clone, exits 0. The 3 commits `main` gained since the base touch neither file nor the citation or derivation scripts, and a re-derivation prints the same 79 commands. No merge was made. - No ablation or reverse verification: the change is comment-only, so there is no behaviour to invert. ## Hypotheses (measured first) 1. **Holds.** 12 dead sites at the tip, 9 in `stack.zod.ts` and 3 in `data/analytics.zod.ts`, equal per file to stage 6's census. 2. **Holds.** Read at 2026-09-29T07:36Z and again at 08:16Z, after the last push and before this PR was opened: all open PRs' full file lists (9 PRs, 166 files at the second read) and the newest `Claim:` on all 11 `pm:dispatched` cards. None names either file, except this card's own claim. 3. **Holds, with nothing to keep.** All 12 sites are comments. No test string, exported string or `describe()` text carries one, and no test or script reads any of them by literal. 4. **Holds.** No generated reference page projects these lines; `check:docs` is green with no regeneration. ## Deviations - None to the file surface: the 12 claimed lines and one changeset, no generated page needed. - Commit trailers follow AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`); the pre-push trailer check passed on every push. ## Acceptance notes **What stays for later stages.** The gate's census at this PR's head (base `0f6dcac5e9` plus this PR) reads **248** dead sites (29 numbers) in `packages/spec/src`. The only `packages/spec/src` change `main` has made since the base (objectstack-ai#20610's migrations entry and registry) adds four live numbers and removes none, so 248 also stands at the tip `7510663c87` plus this PR: - `migrations/` **233**: objectstack-ai#20233 edits the same entry files (PR objectstack-ai#20607 holds `migrations/registry.ts`). - `conversions/registry.ts` **12**: PRs objectstack-ai#20570 and objectstack-ai#20587 hold it. - `integration/connector.zod.ts` **1**: PR objectstack-ai#20587 (objectstack-ai#20287). - `data/api-derivation.ts:163` (objectstack-ai#6259) and `identity/identity.zod.ts:230` (objectstack-ai#8715), **1** each: kept because tests read them by literal, so removing them is form D. **Outside the gate's census: test files.** The gate defers `*.test.ts`. The same six dead numbers still stand at 15 comment sites and 10 test-title strings in `packages/spec/src` test files: - `data/analytics-strictness-batchd.test.ts:96` (comment, objectstack-ai#10194) and its title `:93`. This file is in the `analytics*` set stages 3 and 4 excluded while PR objectstack-ai#20458 held it; `analytics-date-range-two-bound-window.test.ts` and `cube-member-inner-name-retirement.test.ts` were in that set too and are not re-measured here. - The package root: `compose-stacks-action-echo.test.ts:20`, `:34`, `:200` (objectstack-ai#14686) and titles `:176`, `:224`; `compose-stacks-action-key-collision.test.ts:3` (objectstack-ai#14662); `stack-top-level-strict.test.ts:103` (objectstack-ai#10485) and title `:128`; `type-alias-convention.pin.test.ts:257`, `:1572`, `:1937` (objectstack-ai#10485). - `shared/`: `metadata-collection.test.ts:250`, `metadata-url-spelling.test.ts:51`, `:72`, `:168` (objectstack-ai#10485), `:257` (objectstack-ai#10194), title `:254`. `automation/sync-retirement.test.ts:207` (objectstack-ai#10485). - `kernel/`: `manifest-unknown-keys.test.ts`, four titles (objectstack-ai#14192); `metadata-type-schemas.test.ts:422`, a title (objectstack-ai#10194). - Stage 6 took the package root, `shared/` and `automation/` through the gate's census, which never lists a test file, so test-file comment lines there may carry other dead numbers as well. That wider population is not measured here. **Outside `packages/spec/src`.** The same six numbers stand at 44 more sites (`packages/{metadata-protocol,objectql,rest,runtime,cli,core,metadata,qa}`, `examples/`, `scripts/`, `packages/spec/scripts/`), and at 19 sites in `migrations/` (the objectstack-ai#20233 area). **Rung.** The objectstack-ai#10485 retirement also has the ADR-0087 D3 entry `stack-themes-carrier-retired`, which `:423` already names. This PR takes the commit rung, as stages 1–6 did. **Wording, each true of its commit.** `:3037` and `:3194` now read "commit 279431e's same-key refusal": the refusal that commit added, in lines `773a99960a` wrote. `:1233` reads "commit 4d0d944 closed `ManifestSchema`", in a line `c78c9180de` wrote. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
… families states each lesson in words, not tracker numbers (stage 9) (objectstack-ai#20630) Part of objectstack-ai#20233 Stage 9: the remaining semantic-entry families, meaning every ADR-0087 semantic entry that still cited a tracker number. Clause-②: no `os migrate meta` prints each ADR-0087 semantic entry's `surface`, `replacement`, `reason` and `acceptanceCriteria` to the author. This stage covers the 44 entries that still cited a tracker, pull-request, decision-batch or cross-repository number: 39 with a four- or five-digit id, and 5 with a decision-batch number only. In each of them, every sentence now says what the cited ruling, measurement or fix decided (form D). ADR ids stay, and so do the contributor-guide rule references, which are not tracker ids. The `address-location-value-unknown-keys-refused` replacement cited `AGENTS.md #0.1`, which names nothing in AGENTS.md today. It now states the rule it copied from the 2026-09-01 ruling: a consumer-side alias for an off-spec key stays forbidden. - **Text only:** a base-vs-head AST comparison over the 45 changed entry files and `registry.ts` finds no change outside `replacement` / `reason` / `acceptanceCriteria`. That is 48 prose fields per copy, and no `surface`, id, comment, import or token-skeleton change. - **Census (AST instrument, whole tree):** base `682873f201` has 76 prose sites in 39 entries (replacement 3 / reason 73 / acceptanceCriteria 0), 0 surface sites and 28 short numbers. Head has 0 / 0 / 10. The 10 short numbers left are contributor-guide rule references. - **Pin:** `packages/cli/test/migrate-meta-engine-guidance.test.ts` now holds every semantic entry instead of a prefix list, so an entry added later in any family is held on arrival. `REWRITTEN` goes from 203 to 247. Ablation: putting one removed id back into the `turso-` entry (a family the pin did not cover before) turned the pin red; restoring it turned the pin green. - **Generated:** `registry.ts`, `spec-changes.json` and `docs/protocol-upgrade-guide.md`, by their generators. `patch` changeset. ## Verification (head `96b994e472`) - **Tests:** - spec `--project local`: 575/575 files (16917 passed, 1 todo). - spec `--project repo`: 42/42 files (745 passed). - CLI `--project unit`: 234/234 files (3342 passed). - CLI `--project integration`, the three migrate-meta files: 3/3 (13 passed, 1 skipped by the default-range file's own `skipIf`). - spec and CLI typecheck: exit 0, test-layer debt held. - **Gates:** `dispatch-gates.mjs --commands` derives 89 families. 88 exit 0, among them `check:doc-authoring`, `check:generated`, `check:migration-registry`, `check:spec-changes`, `check:upgrade-guide`, `check:issue-citations`, `check:nul-bytes`, `check:api-surface` and `check:authorable-surface`. - `check:dual-build-cjs-loads` is NOT MEASURED: its prerequisite is not met, because packages outside the CLI closure have no `dist/`. Build Core runs it. - `--ran` reconciliation: 89 accounted, 88 run, 1 NOT MEASURED. - **Lint:** eslint over the 47 changed source files: 0 errors, 0 warnings. - **Mergeability:** a driver-free `merge-tree` onto `origin/main` `b80ab579d8` is clean. Main's new commits touch none of these paths. The census method and controls, the source of every citation, and the ablation record are in the dev report on objectstack-ai#20233. ## Acceptance notes - Four verbatim ruling quotes carried a card or batch number: 「217 同意」, 「9266 同意 A」, 「146 同意」, and one reply that also answered another card. Each now keeps only its operative words, or states the decision instead. - The claim fenced off four entries. Two of them are not on `main`: they arrive with PR objectstack-ai#20570 and PR objectstack-ai#20587. The two on `main` carry no site. PR objectstack-ai#20570's incoming `filter-is-empty-lowers-to-empty-operator` carries six tracker ids in `reason`, and the `filter-` family was already held by this pin before this stage. - `ui-list-view-groupbyfield-padded-refused` and `ui-list-view-grouping-field-padded-refused` still cite `AGENTS.md #0.1` in `reason`. That is the same dangling number the `address-` entry had. Both entries are outside this stage's claim and are untouched. - `ui-notification-action-embed-config-retired` names "its ui/ batch 14" of the strictness sweep, a batch number without `#`. It is outside this stage and untouched. - Comment and docblock lines of the entries still cite tracker ids: 127 lines in `semantic/`, 839 across `entries/`. They are form C's, under objectstack-ai#20234, and are untouched. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
… to the commits that decided them (objectstack-ai#20632) Part of objectstack-ai#20594 Clause-②: no ## What changed This is stage 2 of the `domain:cli` lane of the dead-citation sweep: `packages/rest/src/**`. Every comment or docblock site in scope that cited a tracker number answering 404 now cites, in ruling C+D's form C (comment 5749154545 on objectstack-ai#19123), the commit in this repository's history that decided what the line describes, and says in its own words what that commit decided. PR objectstack-ai#20533 is the method and PR objectstack-ai#20624 (stage 1, `packages/runtime`) the precedent this follows line for line. Later stages cover `cli`, `types` and the rest of the lane, so this PR says `Part of` and the card stays open. That is **457 comment sites on 445 lines in 85 files, covering 74 numbers**: the census's 191 sites, 256 more in test comments (which the census defers), and 10 sites whose dead number is the second half of a slash-joined pair the citation grammar does not read (`objectstack-ai#3984/objectstack-ai#6241`, `objectstack-ai#9901/objectstack-ai#10255` four times, `objectstack-ai#10993/objectstack-ai#11235/objectstack-ai#11292`, `objectstack-ai#11235/objectstack-ai#11242` twice, `objectstack-ai#10993/objectstack-ai#11242`, `objectstack-ai#7543/objectstack-ai#15071`). Each rewritten line cites one of **70 distinct commits**. ADR-0076 D11 is the only ADR that records any of these numbers, and it records objectstack-ai#8850 only as the extraction it names as landed in `8664a2c99`, so that commit is the anchor there. No other ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records the decision behind any of these numbers, so every anchor is a commit. The anchors the landed stages already gave the same numbers are reused where the rest sites describe the same decision (30 numbers, for example `79c46da90` for objectstack-ai#9934, `7986d973f` / `311433f6b` for the compound-name retirement, `6a180e42d` for objectstack-ai#13279 and `cf6e0a193` for objectstack-ai#15071), so each number carries one anchor across the tree. Only comments changed. Every touched file keeps its line count (451 lines out, 451 in, over 85 files), so no line citation into these files moves. Six of the 451 lines held no dead site; each is the other half of a sentence that had to change: - `discovery-schema-conformance.test.ts:343` (「(reaffirmed by」 to 「(which commits」, because line 344 now names the two commits that landed the ruling), - `package-door-16019-raw-statement-fault-code.test.ts:51` and `error-response.ts:1485` (a trailing 「PR」 whose number wrapped onto the next line), - `error-response-structured-arm-door-parity.test.ts:463` (「That card added the limb」 to 「That commit」, because line 459's tag now names the commit), - `rest-hook-script-fault-envelope.test.ts:331` (「both sides of that card」 to 「that fix」), - `rest-server.ts:908` (「(objectstack-ai#14409, landed」 to 「(landed as commit」, the sha `3ecb7dc1a` already standing on line 909). **No citation number is added.** Every tracker number on an added line was already on the line it replaces. No PR number stands on an added line. One of the 70 shas is on a removed line, and it was there before: `rest-14078-invalid-date-total-arm.test.ts:19` read 「PR objectstack-ai#14409 (landed `3ecb7dc1a`)」 and now reads 「Commit 3ecb7dc drove」. No code token moves (see the guard below). Three dead comment sites are left on purpose, listed under "The sites left". One more file: a `patch` changeset for `@objectstack/rest`, because the rewritten docblocks ship (see Changeset below). ## Census: `packages/rest`, before and after **Instrument.** The gate's own `node scripts/check-issue-citations.mjs --census --json`, read-only and unchanged, run with the fleet token. Its surface is comment prose in `packages/**/src/**/*.ts` with string literals blanked, and it defers `*.test.ts`. The count is its `allocated-but-absent` findings under `packages/rest/`. Both runs enumerated the whole board (185 pages), so neither read a truncated board. | reading | tree | board | whole-repo `allocated-but-absent` | rest sites | lines | files | numbers | |---|---|---|---|---|---|---|---| | before | base `a186aea996`, run 2026-09-29T10:28:18Z to 10:36:06Z | enumerated, 185 pages, frontier objectstack-ai#20628, 18,455 numbers | 2,015 | **191** | 186 | 14 | 51 | | after | head `93e4d69ba6`, run 11:11:30Z to 11:17:37Z | enumerated, 185 pages, frontier objectstack-ai#20630, 18,457 numbers | 1,764 | **0** | 0 | 0 | 0 | The before count equals the card's 191 at `f11b5f20a2`. The whole-repo drop is 251: this diff's 191, plus the 60 of PR objectstack-ai#20626 (`packages/plugins/plugin-sharing`, 63 to 3), which landed on `main` in between and came in with the merge. No other package moved. **Supplementary instrument, the whole scope.** The census does not read test files or strings, and this stage's scope includes test comments. So a second reading runs the gate's own exported `extractCitations` (whole-file and comment-prose projections) and `classifyCitation` over every `.ts` file under `packages/rest/src` (256 files), against the board enumerated through the gate's own `enumerateBoard`. The lit controls objectstack-ai#20594, objectstack-ai#19123 and objectstack-ai#20624 answered 200 and are on both boards; the dead controls objectstack-ai#13214, objectstack-ai#14541 and objectstack-ai#15071 answered 404 and are on neither. | reading | tree | board | citations | dead | src comment | test comment | src string | test string | |---|---|---|---|---|---|---|---|---| | before, 10:29Z | `a186aea996` | 185 pages, frontier objectstack-ai#20628 | 4,620 | **577** | 191 | 259 | 1 | 126 | | after, 11:21Z | `93e4d69ba6` | 185 pages, frontier objectstack-ai#20631 | 4,174 | **130** | 0 | 3 | 1 | 126 | Its src-comment column equals the census's 191 and 0, which is the control on the second instrument, and a site-by-site comparison of the two before-readings is identical. Resolving comment citations move by one (1,364 to 1,365 in src): `(objectstack-ai#10993/objectstack-ai#11235/objectstack-ai#11292)` became `(objectstack-ai#10993, commit 376c70f, objectstack-ai#11292)`, so the grammar now reads the live `objectstack-ai#11292` that the slash hid. The drop is 447 grammar-read sites; the other 10 rewritten sites are the slash-joined ones the grammar never read. Separately, every one of the 77 numbers was probed on its web endpoint: 76 answer 404 (deleted) and one, #14026, answers 302 to objectstack-ai/objectui#10102 (transferred), which is why it is left (see below). ## Per-number table Sites and files are the dead comment sites in scope at the base, tests and slash-joined halves included. `left` is a site with no deciding commit (see below). `strings kept` counts string-literal sites, which are tokens and stay as they were. Every anchor was read in its message or its diff, not only in its subject: it is the commit that made the change the line describes, and its own message or diff names the number it replaces or adds the citation the line carries. | number | comment sites / files | rewritten | left | strings kept | anchor | |---|---|---|---|---|---| | `objectstack-ai#6037` | 5/3 | 5 | 0 | 0 | `18189983d` | | `objectstack-ai#6122` | 2/2 | 2 | 0 | 0 | `64cd01082` | | `objectstack-ai#6206` | 1/1 | 1 | 0 | 0 | `8e13ca876` | | `objectstack-ai#6216` | 6/2 | 6 | 0 | 2 | `f586f1a89` | | `objectstack-ai#6241` | 10/3 (1 slash-joined) | 10 | 0 | 1 | `83a3b1f2e` | | `objectstack-ai#6259` | 2/1 | 2 | 0 | 0 | `6968885ef` | | `objectstack-ai#6303` | 1/1 | 1 | 0 | 0 | `465c5fc14` | | `objectstack-ai#6306` | 9/5 | 9 | 0 | 3 | `fec784863` | | `objectstack-ai#6307` | 4/2 | 4 | 0 | 0 | `293476148` | | `objectstack-ai#6349` | 4/2 | 4 | 0 | 4 | `2443bb4c4` | | `objectstack-ai#6474` | 1/1 | 1 | 0 | 0 | `18189983d` | | `objectstack-ai#6535` | 3/2 | 3 | 0 | 0 | `a92b1793c` | | `objectstack-ai#6640` | 1/1 | 1 | 0 | 1 | `2ab1257c9` | | `objectstack-ai#6704` | 5/1 | 5 | 0 | 1 | `c3f491626` | | `objectstack-ai#8641` | 1/1 | 0 | 1 | 0 | — | | `objectstack-ai#8850` | 3/3 | 3 | 0 | 0 | `8664a2c99` | | `objectstack-ai#8885` | 6/3 | 6 | 0 | 3 | `30b1c636a` | | `objectstack-ai#8919` | 7/3 | 7 | 0 | 7 | `b5378550e` | | `objectstack-ai#9741` | 12/1 | 12 | 0 | 0 | `2a29caa53` | | `objectstack-ai#9805` | 1/1 | 1 | 0 | 0 | `45862a53d` | | `objectstack-ai#9934` | 19/10 | 19 | 0 | 4 | `79c46da90` | | `objectstack-ai#9967` | 2/2 | 2 | 0 | 4 | `8f266f1cd` | | `objectstack-ai#10063` | 2/2 | 2 | 0 | 1 | `9e04c3e35` | | `objectstack-ai#10178` | 1/1 | 1 | 0 | 0 | `38cf397ea` | | `objectstack-ai#10179` | 0/0 | 0 | 0 | 1 | | | `objectstack-ai#10255` | 18/4 (4 slash-joined) | 18 | 0 | 2 | `6ce58a735` | | `objectstack-ai#10340` | 13/3 | 13 | 0 | 2 | `26f3588fb` | | `objectstack-ai#10345` | 13/6 | 13 | 0 | 6 | `cad8b42f0` | | `objectstack-ai#10350` | 1/1 | 1 | 0 | 0 | `490879ad0` | | `objectstack-ai#10485` | 2/1 | 2 | 0 | 1 | `35ad101bc` | | `objectstack-ai#10537` | 9/3 | 9 | 0 | 1 | `e634ecf6a` | | `objectstack-ai#10888` | 2/2 | 2 | 0 | 0 | `d806081dd` | | `objectstack-ai#11006` | 3/1 | 3 | 0 | 0 | `cccbe51bf` | | `objectstack-ai#11130` | 1/1 | 1 | 0 | 0 | `851909530` | | `objectstack-ai#11235` | 4/2 (1 slash-joined) | 4 | 0 | 0 | `376c70f98` | | `objectstack-ai#11242` | 3/2 (3 slash-joined) | 3 | 0 | 0 | `98ea3443f` | | `objectstack-ai#12144` | 1/1 | 1 | 0 | 0 | `3a04b0125` | | `objectstack-ai#12176` | 11/7 | 11 | 0 | 2 | `7986d973f` | | `objectstack-ai#12194` | 15/5 | 15 | 0 | 4 | `311433f6b` | | `objectstack-ai#12195` | 35/16 | 35 | 0 | 7 | `7986d973f` | | `objectstack-ai#13182` | 2/2 | 2 | 0 | 0 | `5b3ff63cc` | | `objectstack-ai#13197` | 1/1 | 1 | 0 | 0 | `56c093c4d` | | `objectstack-ai#13213` | 2/1 | 2 | 0 | 0 | `4801296e7` | | `objectstack-ai#13214` | 18/6 | 18 | 0 | 14 | `cc837dbfe`, `889ec5b42`, `3d10755f0` | | `objectstack-ai#13244` | 5/2 | 5 | 0 | 1 | `889ec5b42` | | `objectstack-ai#13255` | 4/1 | 4 | 0 | 6 | `43028a8f8` | | `objectstack-ai#13258` | 1/1 | 1 | 0 | 0 | `3d10755f0` | | `objectstack-ai#13279` | 23/5 | 23 | 0 | 5 | `6a180e42d` | | `objectstack-ai#13280` | 13/4 | 13 | 0 | 2 | `add6a1b1c` | | `objectstack-ai#13282` | 1/1 | 1 | 0 | 0 | `43028a8f8` | | `objectstack-ai#13377` | 3/2 | 3 | 0 | 0 | `e10cf3444` | | `objectstack-ai#13378` | 2/1 | 2 | 0 | 0 | `82faea03f` | | `objectstack-ai#13454` | 1/1 | 1 | 0 | 0 | `7ad57e17a` | | `#14026` | 1/1 | 0 | 1 | 0 | — | | `objectstack-ai#14365` | 1/1 | 0 | 1 | 0 | — | | `objectstack-ai#14366` | 14/4 | 14 | 0 | 2 | `53cbad9f7` | | `objectstack-ai#14369` | 3/2 | 3 | 0 | 0 | `a3d5724c8`, `53cbad9f7` | | `objectstack-ai#14389` | 7/3 | 7 | 0 | 7 | `10220a7bf` | | `objectstack-ai#14390` | 1/1 | 1 | 0 | 0 | `9d7f7259f` | | `objectstack-ai#14409` | 2/2 | 2 | 0 | 0 | `3ecb7dc1a` | | `objectstack-ai#14541` | 27/4 | 27 | 0 | 5 | `6d178a408` | | `objectstack-ai#14613` | 2/2 | 2 | 0 | 0 | `81208086a` | | `objectstack-ai#14677` | 1/1 | 1 | 0 | 0 | `a4e4d2d78` | | `objectstack-ai#14683` | 8/2 | 8 | 0 | 0 | `96326040f` | | `objectstack-ai#14691` | 15/2 | 15 | 0 | 2 | `b3a63d32c` | | `objectstack-ai#14704` | 9/3 | 9 | 0 | 2 | `1c7adc73d` | | `objectstack-ai#14723` | 7/4 | 7 | 0 | 4 | `65846bc46` | | `objectstack-ai#14725` | 3/3 | 3 | 0 | 2 | `f5cc78b63` | | `objectstack-ai#14849` | 3/1 | 3 | 0 | 0 | `226e72443` | | `objectstack-ai#14907` | 1/1 | 1 | 0 | 0 | `e1d4f9e3f` | | `objectstack-ai#14908` | 1/1 | 1 | 0 | 0 | `d5cbb44f3` | | `objectstack-ai#15021` | 2/1 | 2 | 0 | 8 | `cc238db8b` | | `objectstack-ai#15034` | 6/2 | 6 | 0 | 0 | `abf9101f1` | | `objectstack-ai#15065` | 1/1 | 1 | 0 | 0 | `1c7adc73d` | | `objectstack-ai#15071` | 23/4 (1 slash-joined) | 23 | 0 | 3 | `cf6e0a193` | | `objectstack-ai#16650` | 1/1 | 1 | 0 | 0 | `001a83b04` | | `objectstack-ai#17058` | 3/1 | 3 | 0 | 4 | `94c930248` | | `objectstack-ai#18546` | 3/2 | 3 | 0 | 3 | `58f60e37e` | | **total** | **460** | **457** | **3** | **127** | **70 distinct commits** | Every cited sha matches exactly one object (`git rev-parse --disambiguate`, count 1 for each of the 70), is a commit, has one parent, and is an ancestor of the base (`merge-base --is-ancestor`, exit 0 for all 70). The checkout is not shallow (`--is-shallow-repository` false); the control leg `13a6cb4ad` exits 0 and the negative control (this branch's first WIP commit, not on `main`) exits 1. Several numbers are the PR number of their own anchor commit (objectstack-ai#6122, objectstack-ai#6303, objectstack-ai#6474, objectstack-ai#11242, objectstack-ai#13213, objectstack-ai#13244, objectstack-ai#13258, objectstack-ai#13282, objectstack-ai#14409, objectstack-ai#14677, objectstack-ai#14908, objectstack-ai#15065, objectstack-ai#16650), so the sha is the same object the number named. **Numbers with more than one anchor, by site:** - `objectstack-ai#13214` (18 sites) was one card with three commits. `cc837dbfe` (the ownership gate, the 2026-08-30 ruling) for the 11 sites that describe the gate; `889ec5b42` for the 5 in `ui-view-route-identity.measurement.test.ts`, the identity measurement it created; `3d10755f0` for the tenancy file's header, the measurement it created; and `rest-server.ts:2247`, 「Driven and reported on objectstack-ai#13214 (PRs objectstack-ai#13244, objectstack-ai#13258)」, now reads 「Measured in commits 889ec5b (identity) and 3d10755 (tenancy)」: those PRs are exactly those two commits. - `objectstack-ai#14369` (3 sites): `a3d5724c8` (the liveness census it recorded) for `rest-server.ts:1172` and `rest-sub-config-parse-not-cast.test.ts:48`. `rest-server.ts:4092` said the zero read sites of `api.documentation` / `api.responseFormat` came from 「the objectstack-ai#14369 census」, but `a3d5724c8` explicitly left `api` out of that census; the zero was measured by `53cbad9f7` (its changeset: no other read site for either key), which is the anchor there. - `objectstack-ai#11235` / `objectstack-ai#11242` / `objectstack-ai#10993`: `376c70f98` derives the discovery `version` in metadata-protocol (objectstack-ai#11235), and `98ea3443f` is objectstack-ai#11242's own squash, which landed the objectstack-ai#10993 ruling on `/health` and the dispatcher's `/discovery`. So 「the objectstack-ai#10993 ruling … reaffirmed by objectstack-ai#11235/objectstack-ai#11242」 now reads 「the objectstack-ai#10993 ruling, landed by commits 98ea344 and 376c70f」 (`rest-server.ts:4528`, `discovery-schema-conformance.test.ts:343-344`). `objectstack-ai#10993`, `objectstack-ai#11292` and `objectstack-ai#11297` answer 200 and stay. - `objectstack-ai#6037` / `objectstack-ai#6474`: one commit, `18189983d` (objectstack-ai#6474 is its PR number), so 「(objectstack-ai#6037 / PR objectstack-ai#6474)」 became 「(commit 1818998)」. **Wordings to check, each true of its commit:** - A commit does not rule. Where a line said a number ruled, it now says what the commit did with the ruling: 「the ruling commit 79c46da landed says it does」, 「the ruling commit cf6e0a1 implemented fences it」, 「the ruling commit 10220a7 implemented」, 「the 2026-08-20 ruling, landed as commit 6ce58a7」, 「recorded in commit 6ce58a7's message (option A)」 (its message reads 「Ruled on objectstack-ai#10255 (2026-08-20, option A)」), and 「question was ruled on 2026-08-20 and landed as commit 6ce58a7」 where the line said 「filed as objectstack-ai#10255」. - `objectstack-ai#14541`'s contract review: 「the objectstack-ai#14541 contract review (condition N)」 now reads 「the contract review of commit 6d178a4 (condition N)」; that commit's message lists the conditions it carries. 「objectstack-ai#14541's §4」 and 「objectstack-ai#14541 §5」 in `error-response-generic-passthrough-object-parity.test.ts` are sections of `error-response-structured-arm-door-parity.test.ts` (the file `6d178a408` created), so they now name that file. 「measured on the objectstack-ai#14541 branch」 reads 「on the branch that landed as commit 6d178a4」. - A line that named a DEFECT by its number now says so: 「Before commit 9e04c3e the draft→active promotion door could not…」, 「Before commit 26f3588 the `/meta` doors decided ORGANIZATION SCOPE from the RAW url」, 「the defect commit 2443bb4 fixed」 and 「would be the defect commit 26f3588 fixed」. - `objectstack-ai#13255`: 「As written for objectstack-ai#13255 this file repaired nothing」 reads 「As first written (commit 43028a8)」, the commit that created the file and answered the measurement; 「CONTEXT-LOST family (objectstack-ai#13255), still unruled」 reads 「first measured by commit 43028a8」 (the ruling on that family never landed, which the line still says). - `objectstack-ai#13214` in the identity file: 「the half objectstack-ai#13214 marks UNMEASURED」 reads 「the half left UNMEASURED until commit 889ec5b」, and 「objectstack-ai#13214 asks for an INDEPENDENT reproduction」 reads 「commit 889ec5b is an INDEPENDENT reproduction」. - 「the objectstack-ai#8885 sweep」 reads 「the sweep behind commit 30b1c63」, the commit that registered the 9 codes the sweep found; 「objectstack-ai#14849 predicted」 reads 「The card behind commit 226e724 predicted」; 「the hazard objectstack-ai#13377 names」 reads 「the hazard commit e10cf34 was written to remove」; 「The concrete harm objectstack-ai#6704 names」 reads 「removed」. - Quoted ruling: `error-response-sandbox-arm-message.test.ts:340` sits inside a verbatim ruling quote, so the commit stands in an editorial bracket (「not from [commit 1c7adc7]'s list」), as PR objectstack-ai#20624 did. - Two markdown tables in comments (`meta-state-route-engine-outage.test.ts:76`, `objectql-slot-consumer-census.test.ts:43`): the rewritten cell is wider than its column, and its padding is reduced rather than widening the four sibling rows. ## The sites left **No deciding commit (3 sites, all in test files, so the census does not see them):** - `meta-object-owd-gate.test.ts:516` (objectstack-ai#8641): 「whether it should stay is objectstack-ai#8641's question」, an open decision. The commit that added the citation calls it a pointer to the open decision card, and no commit decides it. - `rest-sub-config-parse-not-cast.test.ts:321` (objectstack-ai#14365): the `z.partialRecord` question 「deferred to objectstack-ai#14365」 was never taken (`git log -S partialRecord`); `b3a63d32c` made it moot by retiring the record, which the other half of the same line now cites. - `import-integration.test.ts:1043` (#14026): not deleted, TRANSFERRED. The web endpoint answers 302 to objectstack-ai/objectui#10102, the REST read follows the redirect, and the board enumeration does not list it, so the census and the supplementary reading both class it `allocated-but-absent`. The line says how an issue was raised; no commit decides that, so form C has nothing to cite. **String sites kept as tokens (127).** 126 are test titles and test-code strings in 41 files. One is a non-test string: the `note` field of the REST route ledger's `GET /api/v1/meta/object/:name/state/:field` row at `rest-route-ledger.ts:290`, which ends 「(objectstack-ai#10179)」 (see Acceptance notes). ## Mechanical guard: no code token moves The check compares the TypeScript parser's leaf tokens (TypeScript 6.0.3, JSDoc nodes excluded, comments being trivia) of each touched file at base `a186aea996` against the working tree at `93e4d69ba6`, over all 85 touched `.ts` files. Controls mutate the head text in memory only, so nothing on disk moved for them. - Real run: 272,653 base tokens, **0 files with a token change** (exit 0). - Comment-insertion control (`error-response.ts`): 0 files changed (exit 0). - Code-insertion positive control (a declaration in the same file): DIFFER at token 0 (exit 1). - String positive control (the first string literal past offset 2000 of the same file, one character added inside it): DIFFER at token 26 (exit 1). Line balance: every touched file is +N/−N (451/451), and every line count is equal at base and head. A raw scan of the 86 changed files for control bytes finds none (its positive control on a scratch file with a U+0001 byte matches). ## Changeset This change ships bytes, so a `patch` changeset for `@objectstack/rest` is included, in PR objectstack-ai#20624's form and level. It says only that the provenance comments were re-anchored. Measured on the built package: `files[]` is `dist`, `README.md` and `CHANGELOG.md`. After `pnpm --filter @objectstack/rest build`, the rewritten docblocks reach `dist`: for example `53cbad9f7` appears 4 times in `dist/index.d.ts`, and `26f3588fb` 8 times and `b3a63d32c` 5 times in `dist/index.js`. The positive control, the unchanged sentence 「It was VALIDATE-ONLY from objectstack-ai#11637」 of the same `rest-server.ts` docblock whose first line now reads 「[commit 53cbad9] The parsed output is CONSUMED」, is in `dist/index.d.ts` beside it; a negative control phrase appears nowhere. ## Gates (head `93e4d69ba6`) This host has no `flock`, so `os-verify-lock.sh` ran in its declared unlocked mode. Its disclosure, verbatim, from each locked run at this head: ```text os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 47s · declare it in the PR body · pnpm --filter '@objectstack/rest...' build os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 102s (1m42s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=4 os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 76s (1m16s) · declare it in the PR body · pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2 os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/rest exec vitest run --project repo --maxWorkers=2 os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 9s · declare it in the PR body · pnpm --filter @objectstack/rest typecheck ``` The branch merged `origin/main` once (`93e4d69ba6`, merging `542670da6d`) before these runs, as the dispatch orders; `origin/main` has not moved since (read at 11:19Z). The merge brought PR objectstack-ai#20626 and PR objectstack-ai#20587 and touched none of this diff's files. The dependency closure was built first (`pnpm --filter '@objectstack/rest...' build`, 26 packages), then the whole workspace (`turbo run build --filter='./packages/*' --filter='./packages/*/*'`, 71 tasks, 71 successful). - **Tests:** `vitest run --project local`: 227 files, 4,382 tests passed, 50 skipped. `--project repo` (which holds the touched `meta-state-route-doc-spelling.test.ts`): 1 file, 8 tests passed. Together they are all 228 test files of the package, so every touched test file ran. - **Typecheck:** `pnpm --filter @objectstack/rest typecheck` exits 0. `tsc --listFiles` counts 28 `src` files (no tests) under `tsconfig.json` and all 228 test files under `tsconfig.test.json`, which `check:test-typecheck` judges: 0 files, 0 errors, 0 pinned signatures in the ledger. - **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`) exits 0 at `93e4d69ba6` (2026-09-29T11:19:30Z to 11:20:00Z). Not narrowed. - **Citation judging:** `node scripts/check-issue-citations.mjs --base origin/main` exits 0: 19 citations judged across 14 files (18 resolve, 1 resolves as a pull request). These are the live numbers that stay on rewritten lines. It defers `*.test.ts`, so the added-minus-removed count over the whole diff covers the rest: 0 numbers added. - **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at `93e4d69ba6` derived 68 families. All 68 exit 0, and `--ran` over a record carrying each exit code reads 「68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived zero). - `check:dual-build-cjs-loads` and `check:type-check-debt` first exited 3 (PREREQUISITE NOT MET, nothing measured) on the closure-only build; after the whole-workspace build both exited 0. - Among them: `check:doc-authoring`, `check:nul-bytes`, `check:rest-log-declared`, `check:route-envelope`, `check:system-context-census` (106 elevation read sites, the page's 102 symbols held) and `check:issue-citations` (self-test). - **Artifact rosters:** 33 of the 36 non-self-test roster rows exit 0 at `93e4d69ba6`, `check-changeset-fixed` (the one whose roster sits under `.changeset/`) and `check:route-ledger-census` among them. The other three, `check-closing-target-claim`, `check-partof-closing-keyword` and `check-single-claim-paths`, answer 「NOT WIRED」 (exit 2) without a pull request's context; they are run against this PR once it exists and reported on the card. ## Hypotheses (measured first) - **H0 holds.** The filtered census answers 191 dead sites at `a186aea996` (186 lines, 14 files, 51 numbers), equal to the card's count at `f11b5f20a2`: no net drift, although PR objectstack-ai#20601 (merged as `eb4b17c346`, before this base) touches four files in `packages/rest`. - **H1 holds.** After the rewrite the filtered census answers 0. The supplementary reading leaves 3 test-comment sites, the three listed above: an open decision, an untaken option and a transferred issue, none with a deciding commit. No site was held for an open PR: the claim's read and this stage's two reads of the open PRs' file lists (10:27:35Z, 7 open PRs; 11:30:34Z, 8 open PRs) found none touching `packages/rest`. - **H2 holds, by the token guard.** A comment-stripped comparison of every touched file (the parser's leaf tokens, JSDoc excluded) is empty, and its code and string controls fire. The emitted `dist` is not byte-identical, because the docblocks ship, which is why the changeset is `patch`. ## Acceptance notes - **Form D, not touched here.** 127 dead numbers stand inside string literals: 126 in test titles and test-code strings, and one in the `note` of the REST route ledger's legal-next-state row (`rest-route-ledger.ts:290`, 「(objectstack-ai#10179)」), which is ledger data, not an author-shown refusal. Ruling D (no number, the lesson in words) is a string change outside this comment-only scope; the card already carries a form-D stage for the lane. - **A transferred issue among the 404s.** #14026 answers 302 to objectstack-ai/objectui#10102 on its web endpoint. The census classes it `allocated-but-absent` (deleted and transferred are only told apart under `--probe-cause`), and `scripts/check-issue-citations.mjs`'s header says the `transferred` arm has no positive specimen on this tree; this is one. Noted, not filed. - **The grammar does not read a slash-joined number.** `CITATION_RE` refuses a `#` preceded by `/`, so the second number of `#A/#B` is never judged. In `packages/rest/src` six such dead numbers stood at 10 comment sites, all rewritten here; one more, `objectstack-ai#14389` in `objectstack-ai#14095/objectstack-ai#14389`, stands inside a string (`error-response-structured-arm-door-parity.test.ts:187`) and is kept. The same shape PR objectstack-ai#20624 and PR objectstack-ai#20612 reported. Noted, not filed. - **Outside the scope and the census surface.** `packages/rest/vitest.config.ts:21` cites objectstack-ai#17853, which answers 404; `packages/rest/test-typecheck-debt.json`, written by `gen:test-typecheck-debt`, carries objectstack-ai#13470, objectstack-ai#13454, objectstack-ai#13377 and objectstack-ai#13378 in its prose, all 404. Neither is under `src/**`. The other numbers in `vitest.config.ts`, `tsconfig.json` and `tsconfig.test.json` answer 200. - **Two comments stale on their own, not touched.** The anchor research found `rest-server.ts`'s `api` docblock near `:1115` and the 「zero read sites」 sentence at `:4092` both overtaken by `80153f5a4`, whose own acceptance notes record it. This PR re-anchors their citations and leaves their claims alone. - **An attribution corrected by the anchor.** `rest-server.ts:4092` credited its zero-read-site count to 「the objectstack-ai#14369 census」, which (`a3d5724c8`) excluded `api`; it now cites `53cbad9f7`, the commit that measured it. - **Base.** One merge of `origin/main` (`93e4d69ba6`) before the `--base origin/main` run, as the dispatch orders. ## Deviations - Ten sites beyond the census's read grammar carry a slash-joined dead number and are rewritten; six more lines are the other half of a rewritten sentence (listed under What changed). - The whole-workspace build ran with `--concurrency=4`, not 2, to stay inside the ten-minute foreground cap on this host; it took 1m42s. - Anchor research for 33 of the 77 numbers ran in three read-only research subagents; every proposal was verified here against the commit's message or diff, and the wording of each changed line was reviewed and corrected by hand in a second pass. - Commit trailers are AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`), and the pre-push trailer check passed on every push. The merge commit carries git's default message. --- _Generated by [Claude Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_ --------- Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
Part of #20287
Clause-②: no (narrowing). The tombstone refuses
triggers, which parses today.@objectstack/specminor, with the BREAKING banner and the ADR-0087 disposition the ruling sets.Retires
connector.triggers(theConnectorTriggerarray) under ADR-0049, per the director's ruling on the card (letter B; ADR-0041 unchanged, no new ADR).triggersis aretiredKey()tombstone onConnectorBaseSchema. Its prescription names what works today: anapiflow for an external event and ascheduleflow for a scheduled pull, each calling the connector's action in aconnector_actionnode.triggersis deleted: the tombstone refuses every value first, so that rule could no longer be reached.ConnectorTriggerleaves whole. The D2connector-triggers-removed(step 18) carriesretiredAfter17.5.0, the labelmaincarries after the 17.5.0 cut. The D3 isconnector-triggers-retired.triggers[].interval→intervalSecondsrename is absorbed (spec-property-retirement§0).triggersrows collapse into onedeadtombstone row. Generated files are regenerated.metadata-core's per-entry window pins now state the window rule over the live registry, not a snapshot of itsretiredAfterstamps. This first post-cut stamp broke the snapshot.#20287 stays open for its action half.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1