Skip to content

docs(spec): anchor five dead tracker citations to commits, and measure the true clean-up scale - #20226

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19123-citation-anchors
Sep 27, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19123-citation-anchors

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19123
Clause-②: no

What changed

Five lines in packages/spec/src/ui cited two tracker numbers GitHub no longer serves (#16714, #16715: REST 404, no 301, re-probed today against lit controls). They are rewritten on ruling C+D (comment 5749154545 on this card): each line now anchors to the commit in this repository's history that decided it, and says in its own words what that commit decided, so a reader never needs the link.

Line (base 4df101c38) Was Anchored to What the line now says
app.zod.ts:457 (shipped as source) (#16714) commit 4cfc93b802 the filters docblock states no precedence order because objectui's hand-written mirror copied one and accepted a combination the schema refuses
app.zod.ts:504 (shipped as source) EXPORTED (#16714) commit 4cfc93b802 the guard is exported so a mirror chains the schema's own rule; no accept set moves
app-nav-target-exclusivity-export.test.ts:4 [#16714] header tag commit 4cfc93b802, with PR #16862 beside it as a convenience link only the three things that commit decided: export the guard, state no order in the filters docblock, move no accept set
app-nav-target-exclusivity-export.test.ts:243 test title (#16714 ruling) the title now carries the lesson; the comment under it anchors to 4cfc93b802 "the export deliberately moved no accept set"
object-refinement-check-exports.test.ts:469 (#16715) commit b37f0b1767, the diff that added the line the declaration-count hardening closes a residual hole in the pin; no live shadowing existed

No ADR, ruling docblock or scripts/adr-anchors/ entry records either decision (objectNavTargetExclusivity in docs/adr and scripts/adr-anchors: 0 files), so both take the commit-sha rung of C. The two app.zod.ts lines are the two shipped-tarball lines the ruling puts under D first: they now carry no tracker number, only the lesson and a sha. The patch changeset for @objectstack/spec says a docblock citation changed and nothing else.

Prose, comments and one test title only. No assertion changes meaning and no accept set moves.

Premise, re-measured on 4df101c38

  • The ruling's first D act, the refusal string in packages/spec/scripts/manifest-prescription.ts:78, has no live target. That file and check-react-blocks-declaration-parity.ts were retired by 28ce61260. git grep over the whole tree finds the three numbers on 5 lines, all in the table above, and #16697 on 0.
  • #16715 has a durable in-repo object. Commit b37f0b1767 added the very line that cited it, and its message records the lesson: "a residual hole in the pin, not a live shadowing".

Census: the true clean-up scale (read-only, nothing swept)

Population. Every tracker-shaped token in packages/spec/src/** (1,594 tracked files) that names this repository: bare #N and objectstack-ai/objectstack#N, with N of 100 or more. Ordinals (batch #, summon #, Directive #, item # and similar) and 3- or 6-digit colour literals are excluded. Cross-repo tokens (objectui#, cloud#, ui#) are counted separately.

Instrument 1: the REST status of every number (a full census, not a sample)

GET /repos/objectstack-ai/objectstack/issues/N without following redirects, 2026-09-27T11:13Z to 11:16Z, plus 5 fully-qualified numbers at about 12:05Z.

status numbers
probed 1,958 (every distinct in-repo number)
200 1,786
404 172 (8.8%)
301 0

Controls. They were re-probed at the start, after every 250 probes and at the end, 9 checkpoints in all. The lit controls #16862, #16847 and #17698 answered 200 at 9 of 9. The dead controls #16714, #16715 and #16697 answered 404 at 9 of 9. So the channel stayed up for the whole run, and the 404s are real readings.

The dead rate depends on the number's era (bare numbers):

numbers dead
below 5000 0%
6000s 21.2%
7000s 0%
8000s 14.0%
9000s 10.6%
10000s 40.0%
11000s 26.7%
12000s 18.5%
13000s 16.4%
14000s 22.5%
15000s 0%
16000s 14.3%
17000s 10.5%
18000s 7.7%
19000s 3.5%

Contamination bound. Five numbers are cited only as sibling-repo PRs, written bare (for example objectui PR followed by the number). One of them, 8887, is among the 404s, and 8758 is cited both ways. So the in-repo dead count is 170 to 172 out of 1,953 to 1,958.

Cross-repo tokens. objectui (183 distinct) and cloud (36) are NOT MEASURED: this session's REST gate answers 403 for both repositories. hotcrm answered 200 for 7 of 7 (lit control hotcrm#1961: 200).

Instrument 2: who owns the squash commit

GET /repos/objectstack-ai/objectstack/commits/SHA/pulls for every dead number that appears as a squash suffix in origin/main history (8,715 commits, back to 2026-06-22). 11 of the 172 do, and all 11 answer []: the PR object is gone. Controls: b37f0b1767 answers [16847] and 1124c45641 answers [19107] (lit); 7936b29ce5, the card's known-dead #16697, answers []. The other 161 dead numbers carry no squash suffix. They were cards, not PRs, and this instrument does not reach them.

Independent cross-check: check-issue-citations --census

I ran the repository's own citation gate in census mode at about 12:02Z. The board frontier is 20,223 and the board size is 18,050, so 2,173 minted numbers (10.7%) are holes. Tree-wide there are 2,779 unresolvable sites, 664 of them in packages/spec/src.

On the gate's surface (non-test comment prose), the two instruments agree on 661 of those 664 sites. The 3 sites only the gate found use the objectstack-ai/objectstack#N spelling, which my first extraction pass missed; I probed them afterwards and they are included above. My 297 extra comment sites break down as 271 in test files (the gate's declared deferred surface) and 26 re-charter #13135 sites that the gate reads as an ordinal.

What remains after this PR

On this head, packages/spec/src/** still has 1,295 dead-cited sites on 1,277 lines in 327 files, citing 170 numbers. On the base it was 1,300 sites, 1,282 lines, 330 files and 172 numbers.

class sites lines files
non-test comment 691 683 180
non-test string or code (text an author can be shown) 186 184 48
test comment 271 266 103
test string or code (titles, fixtures) 147 144 54

By area: migrations 438, data 239, api 153, ui 107, kernel 105, contracts 81, system 38, conversions 24, shared 20, other 90.

Load-bearing share. I read a seeded random sample of 40 dead-cited lines. 37 of the 40 cite a ruling, a deliberate tolerance or exclusion, a retirement, or a cautionary measurement, which is the dispatch's definition of load-bearing. The other 3 are incidental: a bare Refs, a label, a process note. Applied to 1,277 lines, that is roughly 1,100 to 1,200 load-bearing lines, a sweep the ruling keeps out of a single PR. None of them is rewritten here.

The runtime-text slice is the D half. os migrate meta prints each migration entry's replacement, why: (reason) and verify: (acceptanceCriteria) to the author (packages/cli/src/commands/migrate/meta.ts:523-525). Across packages/spec/src/migrations/**, those strings hold 2,060 tracker-id sites (478 distinct numbers; 178 sites are dead). pnpm check:doc-authoring is green over all of them, for two reasons: its Rule 3 does not recognise those property positions, and its cross-package leg excludes packages/spec/src. I report this to the seat as a finding and do not fix it here.

Every dead number still cited, as number and sites/files, in numeric order. Any line is recoverable with git grep -n on the number under packages/spec/src.

  • #5970 6/3 · #6037 11/5 · #6048 8/3 · #6072 1/1 · #6075 2/2 · #6083 10/7 · #6085 2/1 · #6111 3/3
  • #6148 10/6 · #6206 14/7 · #6216 2/2 · #6238 1/1 · #6239 10/6 · #6259 5/2 · #6276 13/2 · #6279 2/2
  • #6287 18/2 · #6300 2/1 · #6306 1/1 · #6345 32/11 · #6361 20/11 · #6362 9/5 · #6363 12/6 · #6483 6/1
  • #6511 7/3 · #6523 11/5 · #6527 1/1 · #6571 10/2 · #6573 2/2 · #6604 3/2 · #6605 3/1 · #6640 2/2
  • #6704 6/3 · #6723 1/1 · #6725 5/2 · #6745 2/1 · #8460 2/1 · #8495 59/27 · #8656 1/1 · #8696 34/13
  • #8714 4/2 · #8715 47/24 · #8758 9/6 · #8772 11/5 · #8778 1/1 · #8794 5/3 · #8836 4/3 · #8873 10/6
  • #8874 1/1 · #8876 11/7 · #8885 2/1 · #8887 2/2 · #9040 20/6 · #9041 28/5 · #9740 3/1 · #9741 23/2
  • #9933 9/2 · #9934 4/4 · #9972 4/2 · #10165 7/2 · #10194 12/4 · #10238 1/1 · #10264 1/1 · #10274 7/3
  • #10329 10/4 · #10330 3/1 · #10338 6/3 · #10347 5/2 · #10485 43/23 · #10527 3/2 · #10627 12/7 · #10724 47/20
  • #10726 15/9 · #10812 6/5 · #10926 11/6 · #11006 15/3 · #11065 7/3 · #11071 1/1 · #11166 2/1 · #11195 5/3
  • #11215 1/1 · #11284 10/4 · #11328 2/2 · #11330 2/1 · #11331 2/1 · #11332 19/7 · #11333 3/3 · #11350 9/5
  • #11408 3/2 · #11453 1/1 · #11504 5/3 · #11507 8/4 · #11513 2/2 · #11658 6/3 · #11693 2/2 · #11703 3/2
  • #11741 9/4 · #11753 6/2 · #11757 4/2 · #11846 30/10 · #11858 1/1 · #11859 2/1 · #12010 5/2 · #12144 2/1
  • #12165 1/1 · #12176 2/1 · #12194 4/4 · #12245 2/1 · #12248 17/4 · #12380 4/2 · #12493 6/2 · #12868 9/6
  • #12950 3/2 · #12961 10/2 · #13031 3/2 · #13109 1/1 · #13135 70/25 · #13156 4/4 · #13195 3/1 · #13197 1/1
  • #13218 7/2 · #13608 5/2 · #13644 3/2 · #13670 1/1 · #13672 3/2 · #13681 1/1 · #14143 2/2 · #14162 1/1
  • #14192 5/2 · #14312 1/1 · #14365 4/3 · #14369 24/13 · #14419 2/2 · #14426 2/2 · #14474 1/1 · #14526 2/2
  • #14662 4/2 · #14676 22/10 · #14686 7/2 · #14691 50/16 · #14722 4/2 · #14723 1/1 · #14748 2/2 · #14902 2/2
  • #14919 2/2 · #14996 1/1 · #16126 2/2 · #16559 2/1 · #16626 3/3 · #16649 12/2 · #16659 5/4 · #16685 4/2
  • #16786 3/2 · #16864 2/2 · #16867 3/2 · #17014 3/2 · #17058 1/1 · #17081 1/1 · #17124 3/3 · #17147 5/2
  • #17286 1/1 · #17348 1/1 · #17590 1/1 · #17987 13/2 · #18003 1/1 · #18012 10/5 · #18177 7/2 · #18335 1/1
  • #19307 1/1 · #19377 8/4

Gates (head 98edadd03)

  • Derived gate families. node scripts/pm/dispatch-gates.mjs --ran reports 83 derived, 79 run with exit 0, 4 NOT MEASURED and 0 unrun. The four exited 3 (PREREQUISITE NOT MET): check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt. Each needs other packages' dist/, and both attempts to take the shared verify lock for that build ended in queue-timeout (99). This is a declared narrowing: the diff changes comment and test-title text in one package and nothing those four gates read, and CI's Lint & Repo Gates runs them.
  • Roster gates. The six artifact-roster gates whose roster sits under a changed path (check-changeset-fixed, check:meta-url-spelling, check:spec-changes, check:authz-resolver, check:error-code-casing, check:filter-alias-parity) all exit 0.
  • Build, tests, typecheck. pnpm --filter @objectstack/spec build exits 0. The two edited test files pass: 2 files, 141 tests. pnpm --filter @objectstack/spec typecheck exits 0.
  • Citation verdict. node scripts/check-issue-citations.mjs --base 4df101c38 (the diff-scoped verdict) exits 0. The change adds one citation, #16862, and it answers 200.

Acceptance notes

  • This change adds one tracker number: PR feat(spec): export objectNavTargetExclusivity; state no precedence order in the filters docblock #16862, once, in a test docblock, as a convenience link beside the sha it cites. The ruling allows that, and nothing else in the change cites a number.
  • Where this departs from the dispatch. The dispatch said to rewrite any load-bearing dead-cited line found in packages/spec/src beyond the five, in this PR. At the measured 1,277 lines, about 92% of them load-bearing, that is the sweep the ruling keeps out of one PR, so this PR measures and lists them and rewrites none.
  • D's first named act, the refusal string, was retired upstream by 28ce61260. The live D population is the migration-entry text above.
  • Instrument notes, reconciled above and not filed: the gate's ordinal list reads re-charter #N as a non-citation, which hides 26 real citations of #13135. My own first extraction pass missed the owner/repo#N spelling (3 sites).

Generated by Claude Code

…its, not deleted tracker numbers

Five lines in packages/spec/src/ui cited two tracker numbers GitHub no longer
serves. Each now anchors to the commit in this repository's history that
decided it and states in its own words what that commit decided:

- app.zod.ts (two docblock sentences, shipped as source): the filters
  docblock states no precedence order because objectui's hand-written mirror
  copied one and accepted a combination the schema refuses; the guard is
  exported so a mirror chains the schema's own rule (commit 4cfc93b).
- app-nav-target-exclusivity-export.test.ts: the header and one test title
  (the title no longer carries a number; the comment below it anchors).
- object-refinement-check-exports.test.ts: the declaration-count hardening
  closes a residual hole, no live shadowing existed (commit b37f0b1).

Prose, comments and one test title only; no assertion changes meaning.

Claude-Session: https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 — 136 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 805af4f290565955d6e6b56ee46fed45f721d955 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 7816169646105a021c7ac14d03a1773d1c097994 — the merge of head 98edadd03c06f76737180e09c613cd69024305fc into base 805af4f290565955d6e6b56ee46fed45f721d955, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7816169646105a021c7ac14d03a1773d1c097994 && git checkout 7816169646105a021c7ac14d03a1773d1c097994
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 805af4f290565955d6e6b56ee46fed45f721d955 98edadd03c06f76737180e09c613cd69024305fc && git checkout -B drift-repro 805af4f290565955d6e6b56ee46fed45f721d955 && git merge --no-ff 98edadd03c06f76737180e09c613cd69024305fc

node scripts/docs-audit/affected-docs.mjs --json 805af4f290565955d6e6b56ee46fed45f721d955

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 98edadd03c06f76737180e09c613cd69024305fc

Read: card #19123 body and all 6 comments (ruling 5749154545, pointer 5807032824, dispatch 5855287758, dev report 5855787862), PR #20226 object/body/files/commits/check-runs, the full diff against merge-base 4df101c, the four files at the head, anchor commits 4cfc93b and b37f0b1 (stat, message, relevant hunks), retirement commit 28ce612, AGENTS.md (citation rule, changeset step 3, Touched packages/spec, Prime Directive 14), the headers of check-changeset-no-major / check-empty-changeset / check-changeset-fixed and check-issue-citations (surfaces, deferred table, cause table). Ran: a TypeScript-scanner token comparison of app.zod.ts base vs head with trivia dropped, an independent comment-region state machine over the same pair, git grep sweeps at base and head, REST probes of 17 issue numbers and 3 commit-to-PR associations, census arithmetic, 4 site-count reproductions, a bounded CI poll to convergence. NOT MEASURED: a local run of the two test files (the shared checkout has no node_modules; CI Test Core green stands in), the 1,958-probe census totals and era table, the check-issue-citations --census totals and the 40-line sample (spot-checked, not re-run), and the four derived gates the dev declared NOT MEASURED (CI ran them).

① Derived judgments

(a) No accept set or runtime string moves. app.zod.ts base vs head through the TypeScript scanner with comment and whitespace trivia dropped: 3974 tokens each, identical token for token. Independent state machine: the 10 changed lines all lie inside /** ... */ blocks, none contains a comment terminator, and the code text is identical once comments are stripped. Every .describe() string, the two ctx.addIssue messages and the export function objectNavTargetExclusivity declaration are byte-identical. No runtime string changes.

(b) No assertion changes meaning. app-nav-target-exclusivity-export.test.ts changes a header docblock (lines 3 to 9), one it title (line 247: the old suffix "(#16714 ruling)" becomes "deliberately moved no accept set") and the comment under it; the two expect calls in that test are untouched. object-refinement-check-exports.test.ts changes three comment lines (468 to 470) above an unchanged declarations helper. The old title text appears nowhere at the head; at the base its only other occurrence is a prose line in the same header, not a pin. No snapshot, no gate keyed on it titles, and no gate requiring a [#N] test-header tag (the [#id] regex in check-doc-anchors / check-docs-single-h1 is a Markdown heading custom id on the docs surface). Leg 5's docblock extraction anchors on recordId: (head line 430), which precedes both rewritten docblocks (441 to 461 and 496 to 515), and leg 3 matches code lines only, so no assertion reads the changed prose. CI Test Core: success.

(c) Anchors are faithful and on the right rung. 4cfc93b802 is the squash of PR #16862 (commits/4cfc93b802/pulls answers [16862]); its app.zod.ts hunk turns the const arrow into export function objectNavTargetExclusivity, deletes "Precedence: recordId → filters → viewName" and inserts "There is deliberately NO precedence order stated here", and its changeset states "No schema's accept set moves" and that objectui's mirror "copied that first half" of the docblock. That is exactly what the rewritten filters docblock, the EXPORTED (commit 4cfc93b802) line, the test header's three-item list and the it-comment attribute to it. b37f0b1767 is the squash of PR #16847 (association answers [16847]); its hunk adds the declarations helper and the very comment now rewritten, and its message reads "four names, one declaration each — a residual hole in the pin, not a live shadowing", which is what the new comment says. Ruling C's first rung is empty: objectNavTargetExclusivity / target-exclusivity in docs/adr, scripts/adr-anchors, content and .claude: 0 files; the numbers 16862, 16847, 16714, 16715 in docs, scripts/adr-anchors, content and .claude: 0 hits; docs/ has no rulings directory; the guard docblock's ADR-0053 mention is the guard's design philosophy, not the export decision. The commit-sha rung is therefore correct for both. The one PR number, #16862, sits once in a test header expressly labelled a convenience link beside the sha; the shipped lines and the it-comment cite the sha alone. No misattribution, no PR-as-citation.

(d) Ruling D on the two shipped lines. Head 457 to 460: "objectui's hand-written mirror did exactly that, which is why commit 4cfc93b took the ordering out of this docblock" and head 506: "EXPORTED (commit 4cfc93b), one function per refinement, the same posture" carry no tracker number and each states the decision in words (the ordering was removed because a mirror copied it and accepted what the schema refuses; the guard is exported so a mirror chains the schema's own rule), so a reader needs no link. Caveat, non-blocking: the same sentence continues on head line 507, re-wrapped by this diff, with "the check* exports of #16489", a live issue (REST 200) outside the card's measured set; see ③.

(e) The one added tracker number, PR #16862, answers REST 200 (a closed, merged pull request). check-issue-citations lists packages/**/*.test.ts in DEFERRED_SURFACES and applies it as an exclusion in surfaceFor, so the header is not judged; on a judged surface a number that resolves as a pull request classifies resolves-as-pull-request, which is not in FINDING_CAUSES. On the judged surface the diff-scoped run sees #16489 on the re-wrapped app.zod.ts line, which resolves. CI Lint & Repo Gates, which runs pnpm check:issue-citations && node scripts/check-issue-citations.mjs: success.

(f) Census. Method: REST issues/N without following redirects with lit and dead controls at nine checkpoints, and commits/SHA/pulls for squash suffixes; both instruments reproduce today (16862, 16847, 17698: 200; 16714, 16715, 16697: 404; 4cfc93b → [16862], b37f0b1 → [16847], 7936b29 → []). Arithmetic: 1,786 + 172 = 1,958; 172/1,958 = 8.78%; 11 + 161 = 172; base to head deltas (1,300→1,295 sites, 1,282→1,277 lines, 330→327 files, 172→170 numbers) equal the 5 sites, 5 lines, 3 files and 2 numbers the diff removes; the class table sums to 1,295 sites / 1,277 lines; the area table sums to 1,295; the per-number list has 170 unique sorted entries summing to 1,295 sites and omits 16714 and 16715. Spot checks: eight sampled dead numbers (5970, 13135, 19377, 8887, 8758, 6037, 17987, 16575) all 404; site/file counts for 19377 (8/4), 5970 (6/3), 6037 (11/5) and 13135 (70/25) reproduce exactly by git grep -o -w at the head. Tree claims: 1,594 tracked files under packages/spec/src at the head; the three numbers on exactly 5 lines at the base and #16697 on 0; manifest-prescription.ts and check-react-blocks-declaration-parity.ts deleted by 28ce612 and absent at the head; origin/main reaches back to 2026-06-22 (8,720 commits now against the dev's 8,715). The body says "None of them is rewritten here" and the diff touches only the five lines, so nothing claims a sweep the diff does not do.

② Semver level

patch for @objectstack/spec with Clause-②: no is the right level. check-changeset-no-major's header forbids major (none here) and binds a minimum only on a PR that declares clause ② (this one declares no, readably). check-empty-changeset's header rejects an empty frontmatter (this one names the package and bump) and a modified foreign changeset (this file is added by the PR). AGENTS.md step 3: a changeset for anything that publishes, skip-changeset only when nothing published moves; src/**/*.zod.ts is in the package's files, so the docblock ships and a changeset is owed, and with no export, accept set, .describe() or error message moving, nothing above patch is. The changeset body carries no tracker number. The "Touched packages/spec" table names check:docs for a TSDoc edit; no committed artifact carries the field docblock text (the old phrase appears nowhere but app.zod.ts at the head), and the TypeScript Type Check job that runs the generated-artifact gates is green. Check Changeset: success.

③ Boundary flags

Blocking: none
Non-blocking: (1) No sweep beyond the five lines: the ruling says the true scale goes in the PR body and is not swept in one PR; the dispatch's "any further dead-number line the census finds" clause meets a measured 1,277 lines, about 92% load-bearing, and the departure is declared in Acceptance notes. Consistent with the ruling. (2) Migration-entry author-facing strings (2,060 sites, 178 dead) reported as an out-of-scope gate-reach finding beside #19124 rather than fixed: a different area and a different gate; not this card's file surface. (3) Four derived gates NOT MEASURED locally with exit 3 (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt): CI's Lint & Repo Gates and Type Check · debt ledger ran them, both success. (4) #16489 remains on the re-wrapped shipped line 507 and in the test header line 11: live today, outside the card's dead set, but the same sentence the PR rewrote still ends in a tracker number; a candidate for the staged C sweep. (5) The body's "the change adds one citation, #16862" describes the diff, not the gate's judged surface, where the added citation is #16489 and the test header is deferred; both resolve, so nothing turns on it. (6) A local run of the two edited test files is NOT MEASURED here; CI Test Core covers it.

CI at this head: 35 check-runs on 98edadd, converged 12:40Z: 32 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)), 0 failed, 0 pending. All seven required contexts from the main-branch ruleset are success: TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Lint & Repo Gates, Governed Surface Queue Guard. mergeable_state clean, PR still draft. Closing keywords: the body carries Fixes #19123 exactly once and no other closing keyword; "The card this PR closes must claim this branch" and "Part-of PR must not also close its card" are success.

Implemented-by: claude/issue-19123-citation-anchors
Reviewed-by: session_01CiCTczDo7tGhafXjf61dUJ

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 27, 2026 12:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 27, 2026
Merged via the queue into main with commit 66e266c Sep 27, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19123-citation-anchors branch September 27, 2026 13:10
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…acts/ to the commits and ADRs that decided them (stage 1) (objectstack-ai#20326)

Part of objectstack-ai#20234
Clause-②: no

## What changed

This is stage 1 of the staged sweep, covering
`packages/spec/src/kernel/**` and `packages/spec/src/contracts/**` and
nothing else. Later stages cover the other areas, so this PR carries
`Part of`.

Every comment or docblock site in these two areas that cited a tracker
number answering 404 has been rewritten in ruling C+D's form C (comment
5749154545 on objectstack-ai#19123): **160 sites on 151 lines in 45 files, covering 47
numbers (152 lines rewritten)**. Each rewritten line now cites the
object this repository controls that decided the matter:

- an ADR or ruling record where one exists;
- otherwise the commit in `origin/main` history that decided it.

Each line also says in its own words what that object decided. Where
nothing answers, the sentence keeps its reason in words and the number
is gone: that happened for four numbers.

Only comments changed. Every file keeps its line count (152 lines out,
152 in), so no line citation into these files moves. No code token moves
(see the guard below). String literals carrying a dead number are
tokens: 30 such sites are left as they were and listed below.

**No citation number is added.** Every tracker number on an added line
was already on the line it replaces. That includes the two PR numbers
now standing beside their shas as convenience links: PR objectstack-ai#6900 beside
`b5404f496`, and PR objectstack-ai#7211 beside `1507ba356`.

## Census: this stage's two areas, before and after

**Instrument.** This is PR objectstack-ai#20226's instrument: REST `GET
/repos/objectstack-ai/objectstack/issues/N` without following redirects,
over every distinct in-repo number cited in the two areas. The
population is:
- bare `#N`, `objectstack#N`, `framework#N`, and the `pre-#N` /
`post-#N` spellings, with N of 100 or more;
- excluding the ordinal heads the citation gate declares (Prime
Directive, `PD`, decision `batch`) and `summon`.

**Controls.** The lit controls were `objectstack-ai#16862`, `objectstack-ai#16847` and `objectstack-ai#17698`. The
dead controls were `objectstack-ai#16714`, `objectstack-ai#16715` and `objectstack-ai#16697`. They were probed at
the start, after every 100 numbers and at the end: 6 checkpoints per
run. They read 18 of 18 lit (200) and 18 of 18 dead (404) in both runs.

| reading | tree | numbers probed | 200 | 404 | 301 or other | dead
sites | kernel | contracts | lines | files | dead numbers |
|---|---|---|---|---|---|---|---|---|---|---|---|
| before | base `6a6a17b62`, probed 2026-09-27T20:14Z to 20:16Z | 445 |
398 | 47 | 0 | **190** | 106 | 84 | 180 | 45 | 47 |
| after | head (probe at `eda5c6b27`, 2026-09-27T21:24Z to 21:26Z;
source identical at the final head) | 415 | 398 | 17 | 0 | **30** | 18 |
12 | 29 | 14 | 17 |

**Before, by class.**
- 73 non-test docblock sites and 26 non-test line comments.
- 22 test docblock sites and 39 test line comments.
- 28 test string sites (describe and it titles, one assertion message).
- 2 non-test strings.

**After.** Only the 30 string sites remain. There are 0 comment sites.
The head probe found no number newly dead since the base probe.

PR objectstack-ai#20226's area table read kernel 105 and contracts 81 at an earlier
base. This census reads 106 and 84 because it also counts the `pre-#N` /
`post-#N` spelling: 3 dead sites.

## Per-number table

The site counts give comments rewritten and strings left. The kind
column says what each line now cites:
- commit: the commit whose diff made the decision the line describes
(read in each diff, not only in the subject);
- ADR: the recorded decision;
- dropped: the reason is kept in words and the number removed.

| number | sites / files | rewritten / left (string) | anchor | kind |
|---|---|---|---|---|
| `objectstack-ai#5970` | 2/1 | 2/0 | `97e7e3caa`: `ActionSchema.visible` gains the
boolean arm | commit |
| `objectstack-ai#6083` | 1/1 | 1/0 | ADR-0122 phase 2, `53068c130`: find and findOne
pinned to the parsed state | commit (ADR named) |
| `objectstack-ai#6206` | 12/5 | 12/0 | `d7e0b4212`: maintainer ruling 2026-08-07,
enforcement takes the full envelope with no per-site subset. One site
cites `8e13ca876`, the route half that restored the five dropped fields.
Two name the ruling in words where the same block already cites the sha
| commit |
| `objectstack-ai#6216` | 2/2 | 2/0 | `f586f1a89`: one ExecutionContext assembler,
closed field-set pin | commit |
| `objectstack-ai#6300` | 2/1 | 2/0 | `74155c735`: find and findOne accept the author
state | commit |
| `objectstack-ai#6361` | 1/1 | 1/0 | `90bbf2510`: notification-list `cursor` retired
on both halves | commit |
| `objectstack-ai#6362` | 3/2 | 3/0 | `b5404f496` (PR objectstack-ai#6900 beside it): `connector`
keeps the ADR-0010 envelope | commit |
| `objectstack-ai#6363` | 3/1 | 3/0 | `17d095413`: `unreadCount` counts the whole
inbox, not the window | commit |
| `objectstack-ai#6483` | 6/1 | 6/0 | ADR-0005 whitelist, executed by `ee58392e1`:
nine unratified `allowOrgOverride: true` rolled back | commit (ADR
named) |
| `objectstack-ai#6511` (a PR) | 5/1 | 5/0 | `d7e0b4212`, its squash commit | commit |
| `objectstack-ai#6523` | 7/3 | 6/1 | `aa4b90d9a`: sharing and approval enforcement
take the full ExecutionContext | commit |
| `objectstack-ai#6640` | 2/2 | 2/0 | `2ab1257c9`: `preserveAudit` is UPDATE-only,
with a loud INSERT warn | commit |
| `objectstack-ai#6723` (a PR) | 1/1 | 1/0 | `8ad609c69`, its squash commit:
getObject's declared answer | commit |
| `objectstack-ai#6725` | 6/2 | 4/2 | `1507ba356` (PR objectstack-ai#7211 beside it): facade object
writes reach the map its reads use | commit |
| `objectstack-ai#6745` | 2/1 | 2/0 | `7a5ef0008`: the getObject-equals-get
conformance pin | commit |
| `objectstack-ai#8715` | 3/3 | 3/0 | `2c86fe3ea`: the retirement pin form over
`export-origins/` | commit |
| `objectstack-ai#8794`, `objectstack-ai#8836` | 1/1 each | 1/0 each | `1850ebbb0`: measured the
filter-reuse invariant and pinned it | commit |
| `objectstack-ai#10194` | 7/2 | 6/1 | `2306a765c`: theme and analytics_cube bound at
the /meta door | commit |
| `objectstack-ai#10238` | 1/1 | 1/0 | none: whether cube authoring is live end to end
is still its own measurement (`559041d39` leaves it open) | dropped |
| `objectstack-ai#10338` | 2/1 | 1/1 | `d2619fd0c`: `ApiEndpoint.target` optional, the
publish gate holds the requirement | commit |
| `objectstack-ai#10485` | 5/2 | 5/0 | `35ad101bc`: `themes` carrier and ThemeSchema
retired | commit |
| `objectstack-ai#10627` | 3/2 | 3/0 | `be21955ba`, whose message records that
controlled census | commit |
| `objectstack-ai#10724` | 16/5 | 15/1 | `be21955ba`: nine dead `contributes` members
tombstoned | commit |
| `objectstack-ai#10726` | 6/3 | 4/2 | `bc56e1881`: `contributes.routes` retired,
ruled Option B | commit |
| `objectstack-ai#10812` | 2/2 | 2/0 | none: the cloud leg's clean close is kept as
its date (2026-08-24, which `be21955ba` records) | dropped |
| `objectstack-ai#11071` | 1/1 | 1/0 | `50fb191dc`: `os generate` file names derived
from the registry, with the parity pin | commit |
| `objectstack-ai#11330` | 2/1 | 1/1 | `a9ee98992`: trust-tier text states
publish-gate-only enforcement | commit |
| `objectstack-ai#11331` | 2/1 | 2/0 | none: an open "tracked on" pointer, and the
enforce leg is unbuilt. The lines now say so | dropped |
| `objectstack-ai#11332` | 9/2 | 8/1 | `dce5cd4f0`: three dead manifest containers
retired | commit |
| `objectstack-ai#11333` | 1/1 | 1/0 | `aaacf1d5c`: the commit that corrected the
permissions half | commit |
| `objectstack-ai#11350` | 1/1 | 1/0 | `ece4dad31`, which records the 2026-08-23
entry-nameability ruling | commit |
| `objectstack-ai#11504` | 1/1 | 1/0 | `f90e82024`: `FLOW_INPUT_SCHEMA_INVALID`
registered | commit |
| `objectstack-ai#11741` | 5/2 | 4/1 | `b706af987`: `SendEmailInput.organizationId` |
commit |
| `objectstack-ai#11846` | 11/3 | 7/4 | `0c2334f6c`: preview mode retired | commit |
| `objectstack-ai#12010` | 5/2 | 3/2 | none: that `ConnectionEngineLike` inventory is
described in words | dropped |
| `objectstack-ai#12165` | 1/1 | 1/0 | `b307bfd2a`: the glob-discovery disposition
recorded beside `filePatterns` | commit |
| `objectstack-ai#12248` | 19/4 | 15/4 | `8425c17cc`: the five ruled engine members
adopted, getObject typed | commit |
| `objectstack-ai#13135` | 9/7 | 8/1 | `9e0ba21a1`: paper customization protocol
retired. ADR-0126 section 6 wall 4 stays cited where the line had it |
commit (ADR named) |
| `objectstack-ai#13608` | 2/1 | 2/0 | `fc9ba76a5`: eligibility held at redemption |
commit |
| `objectstack-ai#14143` | 2/2 | 2/0 | `f19475c0a`: the handler-face
`ctx.recordLoadDenied` signal | commit |
| `objectstack-ai#14192` | 4/1 | 0/4 | none rewritten: test titles only | (strings) |
| `objectstack-ai#14722` | 2/1 | 2/0 | `23c72be3c`: pinned the measurement that
refuted that card's premise | commit |
| `objectstack-ai#16559` | 2/1 | 1/1 | `c7aca0dce`: ResumeFailureReport declared once
| commit |
| `objectstack-ai#16786` | 3/2 | 2/1 | `6059b29c0`: `updateById` declares its answer |
commit |
| `objectstack-ai#17147` | 5/2 | 3/2 | `aaacf1d5c`: the granted permission set is
registered and refuses nothing | commit |
| `objectstack-ai#18335` | 1/1 | 1/0 | ADR-0090 D10's 2026-09-16 note: an API key is a
credential | ADR |

Every cited sha resolves to exactly one commit, and every one is an
ancestor of `origin/main` (38 shas, each `merge-base --is-ancestor` exit
0).

## The 30 string sites left as tokens

- **Test titles and one assertion message (28 sites).**
- contracts: `data-engine.test.ts` (4), `objectql-engine.test.ts` (2),
`email-service.test.ts`, `resume-failure-report.pin.test.ts`,
`scoped-context.test.ts` and `sharing-service.test.ts` (1 each).
- kernel: `manifest-unknown-keys.test.ts` (4), `manifest.test.ts` (4),
`preview-mode-retirement.test.ts` (4, one of them an assertion message),
`plugin-runtime-tier-truthful-text.test.ts` (3), and 1 each in
`metadata-customization-retirement.test.ts`,
`metadata-type-api-registration.test.ts` and
`metadata-type-schemas.test.ts`.
- **Non-test strings (2 sites).** Two `why:` strings on rows of the
exported `METADATA_ROUNDTRIP_CASES` table in
`contracts/metadata-service-roundtrip-conformance.ts` (lines 194 and
202). They ship in the package as data. No runtime path prints them to
an author: both test drivers title each case by its `id`. So they are
not author-shown text in the form D sense, and they are not rewritten
here.

## Mechanical guard: no code token moves

The check is a comments-stripped token comparison, base `6a6a17b62`
against the head. It uses the TypeScript parser's leaf tokens, so
template literals are scanned in context, and it excludes JSDoc nodes.
It ran over all 45 touched `.ts` files.

- Real run: 60,827 base tokens, **0 files with a token change** (exit
0).
- Comment-insertion control: 0 changes, as expected (exit 0).
- Positive control (a declaration inserted): 1 file flagged (exit 1).
- Positive control (one digit changed inside a test-title string): 1
file flagged (exit 1).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/spec`
is included; it says only that provenance comments were re-anchored.

Measured on the built package: rewritten docblocks reach
`dist/**/*.d.ts`. For example, `8425c17cc`, `17d095413`, `aa4b90d9a` and
`fc9ba76a5` each appear in 1 declaration file, and the positive control,
a pre-existing `notification-service` docblock sentence, appears in
`dist/contracts/index.d.ts`. Rewritten comments also reach the bundled
`.js`: `be21955ba` appears in 20 files. The `src/**/*.zod.ts` sources
ship verbatim through `files[]`.

## Gates (head `ebbea0b6e`)

- **Citation judging pass, run as CI runs it:** `pnpm
check:issue-citations && node scripts/check-issue-citations.mjs` exits
0. It judged 23 citations: 21 resolve and 2 resolve as pull requests
(the two convenience links).
- **Doc authoring:** `pnpm check:doc-authoring` exits 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` derived 86 families, and all 86 exit
0. `--ran` reports 86 run, 0 NOT MEASURED, 0 unrun, and exits 0. Five of
them (`check:doc-formula-expressions`, `check:dual-build-cjs-loads`,
`check:i18n`, `check:lean-entry-closure`, `check:type-check-debt`) first
exited 3, PREREQUISITE NOT MET. They exited 0 after a full `turbo run
build` of `./packages/*` (71 tasks, exit 0, under the shared verify
lock).
- **Build, tests, typecheck:**
  - `pnpm --filter @objectstack/spec build` exits 0.
- `vitest run src/kernel src/contracts` in `packages/spec`: 99 files and
1,608 tests pass. That covers all 24 touched test files and every test
here that reads contract source text.
- `pnpm --filter @objectstack/spec typecheck` exits 0, including
`check:test-typecheck`.

## Acceptance notes

- **Base.** The branch is 5 commits behind `origin/main` (`4e0f72e8d`).
None of those commits touches `kernel/`, `contracts/` or any file here,
so there was no merge.
- **No author-shown string in scope.** Nothing in these two areas is
form D's; the migration-entry fields belong to objectstack-ai#20233.
- **Instrument note, not filed.** The citation gate reads `pre-#N` and
`post-#N` as a cross-repository qualifier (`pre-`), so it never judges
them. In these two areas that is 18 sites at base, 3 of them dead. This
census counted them by hand.
- **`framework#N` is NOT MEASURED as a repository.**
`objectstack-ai/framework` answers 403 to this session's REST gate. The
five numbers cited that way resolve 200 in this repository. None of them
is in the dead set.
- **Seat 2's pointer.** The 21 `objectstack-ai#13003` notes in
`packages/spec/liveness/permission.json` are outside this stage, as the
claim records.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…ts that decided them (stage 2) (objectstack-ai#20342)

Part of objectstack-ai#20234
Clause-②: no

## What changed

This is stage 2 of the staged sweep. It covers
`packages/spec/src/api/**` and nothing else, and it leaves out
`packages/spec/src/api/rest-server.zod.ts`, which seat 2's objectstack-ai#20295 claim
holds. Later stages cover the other areas, so this PR says `Part of`.

Every comment or docblock site in scope that cited a tracker number
answering 404 has been rewritten in ruling C+D's form C (comment
5749154545 on objectstack-ai#19123). That is **107 sites on 105 lines in 22 files,
covering 30 numbers**. Each rewritten line now cites the commit in
`origin/main` history that decided what the line describes, and it says
in its own words what that commit decided. No ADR or ruling-record file
in `docs/adr/` or `scripts/adr-anchors/` names any of the 32 dead
numbers, so every anchor is a commit. Nothing was dropped: every number
in scope had a deciding commit. One more comment site is left on
purpose, because its number belongs to another repository (see
Acceptance notes).

Only comments changed. Every source file keeps its line count (112 lines
out, 112 in), so no line citation into these files moves. Seven of those
112 lines carry no number; they are the second half of a sentence that
had to be reflowed. No code token moves (see the guard below). The 34
string-literal sites that carry a dead number are tokens, so they are
left as they were and listed below.

**No citation number is added.** Every tracker number on an added line
was already on the line it replaces. No PR number stands beside a sha.

Two more kinds of file change, both mechanical:
- **Regenerated reference pages.** Five of the rewritten docblock lines
project into `content/docs/references/api/` (`dispatcher.mdx`,
`error-code-ledger.mdx`, `plugin-rest-api.mdx`). `check:docs` proved
those three pages stale, and `pnpm --filter @objectstack/spec
check:generated --fix` regenerated only them. The diff is five lines,
each the same substitution as its source line.
- **A `patch` changeset** for `@objectstack/spec` (see Changeset below).

## Census: `api/`, before and after

**Instrument.** This is stage 1's instrument. It sends REST `GET
/repos/objectstack-ai/objectstack/issues/N` without following redirects,
for every distinct in-repo number cited in `packages/spec/src/api`. The
population is:
- the citation gate's own `CITATION_RE`, kept when the qualifier is
none, `objectstack`, `objectstack-ai/objectstack`, `framework`, `pre-`
or `post-`;
- N of 100 or more;
- excluding the ordinal heads the gate declares, and `summon`.

Each site is classified by the TypeScript parser as a line comment, a
docblock or a string.

**Controls.** The lit controls were `objectstack-ai#16862`, `objectstack-ai#16847` and `objectstack-ai#17698`. The
dead controls were `objectstack-ai#16714`, `objectstack-ai#16715` and `objectstack-ai#16697`. They were probed at
the start, after every 100 numbers and at the end, which is 5
checkpoints per run. They read 15 of 15 lit (200) and 15 of 15 dead
(404) in both runs.

| reading | tree | numbers probed | 200 | 404 | 301 or other | dead
sites, all of `api/` | in scope | `rest-server.zod.ts` | in-scope lines
| in-scope files | dead numbers in scope |
|---|---|---|---|---|---|---|---|---|---|---|---|
| before | base `21ab41041`, probed 2026-09-27T22:56Z to 22:58Z | 351 |
319 | 32 | 0 | **153** | 142 | 11 | 140 | 23 | 31 |
| after | head `22a00c42d`, probed 2026-09-27T23:10Z to 23:12Z; `.ts`
sources identical at the final head | 334 | 319 | 15 | 0 | **46** | 35 |
11 | 35 | 9 | 14 |

**Before, in scope, by class.** 34 non-test docblock sites and 30
non-test line comments. 7 test docblock sites and 37 test line comments.
33 test string sites (describe and it titles). 1 non-test string.

**After, in scope.** 34 string sites and 1 comment site remain. The
comment site is `export-job-family-retirement.test.ts:25`, which names
an objectui PR (see Acceptance notes). The head probe found no number
newly dead since the base probe: 319 numbers answer 200 in both runs.

PR objectstack-ai#20226's area table read `api` 153 at an earlier base, and this
census reads the same 153 at `21ab41041`. Of those, 11 sites sit in
`rest-server.zod.ts` (`objectstack-ai#14369` twice and `objectstack-ai#14691` nine times, all
comments). They wait for objectstack-ai#20295 to land. `pre-#N` spellings do occur in
`api/`: the at-tier review measured 15 sites on 15 lines in 13 files at
the head, for example `discovery.zod.ts:913` `pre-objectstack-ai#4828`. All 15 numbers
answer 200, so the gate's blind spot for that spelling (objectstack-ai#20330) hides no
dead site in this stage. _(Corrected by the `domain:spec` seat 4 at
2026-09-28T00:48Z, from the at-tier review record 5861396181. The draft
said that no such spelling occurs.)_

## Per-number table

The counts are in-scope sites and files. `rewritten / left` gives
comment sites rewritten and string sites left. Every anchor was read in
its diff or message, not only in its subject: it is the commit that made
the change the line now describes, never a later refactor or a commit
that merely mentions the number.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#6037` | 5/2 | 3/2 | `18189983d`: declares
`DataProtocol.validateData`, the validate-only operation, under objectstack-ai#4633
ruling D |
| `objectstack-ai#6239` | 4/2 | 3/1 | `f549a0d4a`: retires `ViewProtocol` and its ten
request/response schemas (ADR-0049, route 3) |
| `objectstack-ai#6287` | 18/2 | 17/1 | `84c86fb45`: `preview` and `trial` fold to
`sandbox` by declaration, and the fold table is typed total over
`EnvironmentType` |
| `objectstack-ai#6306` | 1/1 | 1/0 | `fec784863`: the direct-mount routes read the
one API base |
| `objectstack-ai#6361` | 5/3 | 4/1 | `90bbf2510`: the notification-list `cursor` is
retired on both halves (maintainer ruling 2026-08-07, Option A) |
| `objectstack-ai#6363` | 3/2 | 3/0 | `17d095413`: `unreadCount` counts the whole
inbox, not the window |
| `objectstack-ai#6704` | 4/1 | 3/1 | `c3f491626`: `runAutomations` declares the
default the import route applies, with the agreement pin |
| `objectstack-ai#8885` | 2/1 | 2/0 | `30b1c636a`: registers the nine REST wire codes
that sweep found |
| `objectstack-ai#9740` | 3/1 | 2/1 | `11b779e0f`: declares `getMetaItemLayered`. The
two cross-references to its test block now name the block by member |
| `objectstack-ai#9741` | 23/2 | 16/7 | `2a29caa53`: records the 2026-08-18 maintainer
ruling. `previewDrafts` and `state` are declared, and `environmentId`
stays transport-level |
| `objectstack-ai#9934` | 4/4 | 4/0 | `79c46da90`: the producer-side `userMessage`
channel |
| `objectstack-ai#10264` (objectui) | 1/1 | 0/0 | not this repository's number: see
Acceptance notes |
| `objectstack-ai#10330` | 3/1 | 1/2 | `b9e9227e3`: declares `mappingName` on
`ImportRequestSchema` |
| `objectstack-ai#10338` | 4/2 | 3/1 | `d2619fd0c`: `ApiEndpoint.target` is optional,
and the publish gate holds the flow requirement |
| `objectstack-ai#10726` | 2/2 | 2/0 | `bc56e1881`: retires `contributes.routes`,
ruled Option B (stage 1's anchor too) |
| `objectstack-ai#11006` | 12/2 | 10/2 | `cccbe51bf`: declares `publishMetaItem` under
the 2026-08-22 maintainer ruling, option B |
| `objectstack-ai#11453` | 1/1 | 1/0 | `1a47a5368`: `ack()` refuses a row that is not
`in_flight` |
| `objectstack-ai#11504` | 4/2 | 2/2 | `f90e82024`: registers
`FLOW_INPUT_SCHEMA_INVALID`, the contract half (stage 1's anchor too) |
| `objectstack-ai#11846` | 1/1 | 1/0 | `0c2334f6c`: the preview-mode retirement, whose
test states the same assertion-set reasoning (stage 1's anchor too) |
| `objectstack-ai#11858` (a PR) | 1/1 | 1/0 | `1a47a5368`, its squash commit: `ack()`
takes the shared `DELIVERY_NOT_ELIGIBLE` spelling |
| `objectstack-ai#11859` | 2/1 | 2/0 | `d9cf78eaa`: `ack()` binds the claim credential
in its compare-and-set |
| `objectstack-ai#12194` | 1/1 | 1/0 | `311433f6b`: declares the item-name grammar and
refuses it loudly at the publish door |
| `objectstack-ai#13135` | 3/2 | 3/0 | `9e0ba21a1`: retires the paper
metadata-customization protocol (stage 1's anchor too) |
| `objectstack-ai#13197` | 1/1 | 1/0 | `56c093c4d`: the in-memory driver enforces
field-level `unique` |
| `objectstack-ai#14474` | 1/1 | 1/0 | `df657d9df`: the install-time namespace
conflict refusal carries an ADR-0112 envelope |
| `objectstack-ai#14691` | 16/1 | 4/12 | `b3a63d32c`: retires the ten inert
`RestServerConfig` keys. All 16 sites are in `rest-server.test.ts`; the
9 in `rest-server.zod.ts` are out of scope |
| `objectstack-ai#14723` | 1/1 | 1/0 | `65846bc46`: lands the 2026-09-03 maintainer
ruling, so a unique-constraint refusal has one wire spelling,
`UNIQUE_VIOLATION`, on every route |
| `objectstack-ai#14748` | 2/2 | 2/0 | `92b5d7f00`: registers `NAMESPACE_CONFLICT`
(its diff wrote the row this line glosses) |
| `objectstack-ai#16649` | 12/2 | 11/1 | `613bfbd3d` for 9 sites: registers the
fourteen remaining `door: 'none'` codes. `44c917a47` for 2 sites: widens
the vocabulary gate's face to every published package and retires
`boot-refusal` (each of its diffs wrote the line it now anchors) |
| `objectstack-ai#17058` | 1/1 | 1/0 | `94c930248`: parses the dataset-query selection
at the door, with its leniency sweep |
| `objectstack-ai#19307` | 1/1 | 1/0 | `8f6d83147`: the `sys_permission_set`
duplicate-name refusal carries `UNIQUE_VIOLATION` |

Every cited sha resolves to exactly one commit (`git rev-parse
--disambiguate`, count 1), and every one is an ancestor of the base
(`merge-base --is-ancestor`, exit 0). That is 30 distinct shas.

Two wordings to check, both true of their commit:
- `error-code-ledger.zod.ts:919`: 「the contract review that passed
commit 1a47a53 ruled option B」. The review record sat on PR objectstack-ai#11858,
which answers 404 today. The line keeps that review's ruling as stated
before and names the commit that landed it.
- `discovery.zod.ts:291`: 「route B toward the single API base that
commit fec7848 gave the direct-mount routes」. objectstack-ai#6633 is live and stays,
and the dead goal it pointed at is now named by the commit that
delivered it.

## The 34 string sites left as tokens

- **Test titles (33 sites).** `protocol.test.ts` 12,
`rest-server.test.ts` 12, `export.test.ts` 3,
`error-code-ledger.test.ts` 2, `validate-data.test.ts` 2, and 1 each in
`discovery.test.ts` and `endpoint.test.ts`.
- **Non-test string (1 site).** `error-code-ledger.zod.ts:1692`, the
`reason` of the `FLOW_INPUT_SCHEMA_INVALID` row in the exported
`PROVENANCE_WAIVERS` table (it cites `objectstack-ai#11504`). It ships in the package
as data. The one reader, `scripts/check-error-code-provenance.ts`,
checks the table and never prints a `reason` to an author. So it is
neither a comment nor author-shown text in the form D sense, and it is
not rewritten here.

No author-shown text was found in `api/`: nothing here is objectstack-ai#20233's form
D.

## Mechanical guard: no code token moves

The check is a comments-stripped token comparison, base `21ab41041`
against head `22a00c42d` (the later commits add only the changeset and
the regenerated pages). It uses the TypeScript parser's leaf tokens, so
template literals are scanned in context, and it excludes JSDoc nodes.
It ran over all 22 touched `.ts` files.

- Real run: 88,201 base tokens, **0 files with a token change** (exit
0).
- Comment-insertion control: 0 files changed, as expected (exit 0).
- Positive control (a declaration appended): 1 file reads DIFFER (exit
1).
- Positive control (one digit changed inside a `rest-server.test.ts`
test-title string): 1 file reads DIFFER (exit 1).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/spec`
is included. It says only that the provenance comments were re-anchored.

Measured on the built package: 8 of the touched sources are
`src/**/*.zod.ts`, which `files[]` ships verbatim. The rewritten
docblocks also reach `dist`: `2a29caa53`, `cccbe51bf`, `84c86fb45`,
`613bfbd3d` and `90bbf2510` each appear in 1 declaration file, and
`613bfbd3d` and `79c46da90` each appear in 6 bundled `.js` files. The
positive control, a pre-existing `protocol.zod.ts` docblock sentence,
appears in `dist/api/index.d.ts`.

## Gates (head `24d9fba68`)

- **Citation judging pass, run as CI runs it:** `pnpm
check:issue-citations && node scripts/check-issue-citations.mjs` exits
0. The self-test passes 73 cases in 7 batteries. The live run judged 13
citations across 8 files, and all 13 resolve. These are the live numbers
left on changed lines, such as objectstack-ai#4633, objectstack-ai#4828, objectstack-ai#12004, objectstack-ai#11679 and objectstack-ai#16404.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at the final head derived 111
families, and all 111 exit 0. `--ran` reports 111 run, 0 NOT MEASURED, 0
unrun, and exits 0.
- At the earlier head, before the reference pages were regenerated, the
list held 87 families. `check:docs` exited 1 there; that is what proved
the three pages stale.
- Four gates (`check:doc-formula-expressions`,
`check:dual-build-cjs-loads`, `check:lean-entry-closure`,
`check:type-check-debt`) first exited 3, PREREQUISITE NOT MET. They exit
0 after a full `turbo run build` of `./packages/*` (71 tasks, exit 0,
under the shared verify lock).
- **Build, tests, typecheck and lint:**
  - `pnpm --filter @objectstack/spec build` exits 0.
- `vitest run --maxWorkers=2 src/api` in `packages/spec`: 47 files and
1,518 tests pass. That covers all 13 touched test files.
- The three spec script suites that read `api/` source text
(`check-error-code-provenance`, `file-description`, `schema-index`)
pass: 3 files, 144 tests.
- `pnpm --filter @objectstack/spec typecheck` exits 0, including
`check:test-typecheck` (53 files, 255 errors, 142 pinned signatures
held).
- `eslint --no-inline-config --format json` over the 22 touched `.ts`
files: 22 files, 0 errors, 0 warnings. All 22 are in eslint's own
population (`isPathIgnored` false for each). The config never enables
type-aware linting, so a comment edit here cannot move the verdict on
any untouched file. The repo-wide `pnpm lint` is CI's run.

## Acceptance notes

- **The objectui PR number.** `export-job-family-retirement.test.ts:25`
reads 「objectui retired its side first (objectui#10247, merged as
objectui PR objectstack-ai#10264)」. The number belongs to objectui's board, but the
citation grammar reads `objectui PR objectstack-ai#10264` as a bare number of this
repository, so the census counts it as dead here (it is 404 on this
board). `objectstack-ai/objectui` answers 403 to this session, on both
REST and the web page, so whether that PR is live is NOT MEASURED. The
line is left unchanged. This is the same grammar family as objectstack-ai#20330's
`pre-` / `post-` blind spot, a qualifier the grammar does not recognise,
and it is named there, not filed.
- **Base.** The branch is 2 commits behind `origin/main` (`d3958bac6`,
read at 2026-09-28). Neither commit touches `packages/spec/src/api` or
`content/docs/references/api`, and a no-driver `merge-tree` of the head
onto `d3958bac6` exits 0. So there was no merge.
- **objectstack-ai#20295's branch.** It edits `rest-server.test.ts` too, in hunks at
base lines 114 to 136, 152 to 185 and 663 to 679. This PR's four comment
edits there are at lines 198, 206, 266 and 371. A no-driver `merge-tree`
of this head with that branch's head `2e575f156` exits 0.
- **Surface.** The regenerated reference pages under
`content/docs/references/api/` are the one set of files outside
`packages/spec/src/api/**` besides the changeset. They are generator
output projected from the rewritten docblocks, and the required
`TypeScript Type Check` job reds without them.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…its that decided them (stage 3) (objectstack-ai#20533)

Part of objectstack-ai#20234
Clause-②: no

## What changed

This is stage 3 of the staged sweep. It covers
`packages/spec/src/data/**` and nothing else. It leaves out the files an
open PR or an in-flight claim holds: `data-engine.zod.ts`,
`data-engine.test.ts`, `hook.form.ts`, `analytics*.ts`,
`cube-member-inner-name-retirement.test.ts`, `driver/turso.zod.ts` and
`filter-subtree-provenance.ts`, as the claim names them. It also leaves
out four files that open PRs started editing after the claim:
`driver/turso.test.ts` (PR objectstack-ai#20504, objectstack-ai#20437's, opened 2026-09-28T20:08Z),
`object.form.ts` (PR objectstack-ai#20519, objectstack-ai#20432's, 21:55Z), `object.zod.ts` (PR
objectstack-ai#20521, objectstack-ai#20494's, 22:10Z) and `filter-logic-conformance.ts` (PR objectstack-ai#20523,
objectstack-ai#20444's, 22:39Z). See Acceptance notes. Later stages cover the other
areas, so this PR says `Part of`.

Every comment or docblock site in scope that cited a tracker number
answering 404 has been rewritten in ruling C+D's form C (comment
5749154545 on objectstack-ai#19123). That is **163 sites on 161 lines in 44 files,
covering 40 numbers**. Each rewritten line now cites the commit in
`origin/main` history that decided what the line describes, and it says
in its own words what that commit decided.

No ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/`
records the decision behind any of the 43 dead numbers in scope.
ADR-0104 names objectstack-ai#12380 only as a reference, and ADR-0055 states the rule
that objectstack-ai#8772's ruling enforced, not the ruling itself. So every anchor is
a commit: **38 distinct shas**. One number was dropped rather than
anchored: objectstack-ai#17286, a tracking card that recorded an axis as undecided,
under which no commit landed. The sentence keeps its reason in words.

Three comment sites in scope are left on purpose (see Acceptance notes).
Two are the `[objectstack-ai#6259]` marker in `api-derivation.ts:163`, which a test
string reads, and the test comment that names that marker. The third is
`field.zod.ts:370`, whose `objectstack-ai#6111` is objectui's number.

Only comments changed. Every source file keeps its line count (174 lines
out, 174 in, over 45 files), so no line citation into these files moves.
Thirteen of those 174 lines held no dead citation. Eleven are the other
half of a sentence that had to be reflowed or rewritten. One is a table
header (`value-roundtrip-conformance.ts:20`, 「card」 to 「card or commit」,
because its row now holds a commit). One is `api-derivation.ts:164`,
which now carries the `[objectstack-ai#6259]` sentence's commit. No code token moves
(see the guard below). The 41 string-literal sites that carry a dead
number are tokens, so they are left as they were and listed below.

**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.

Two more kinds of file change, both mechanical:
- **One regenerated reference page.** Two of the rewritten docblock
lines (`feed.zod.ts:15`, `:18`) project into
`content/docs/references/data/feed.mdx`. `check:docs` proved that page
stale, and `pnpm --filter @objectstack/spec check:generated --fix`
regenerated only it. The diff is two lines, each the same substitution
as its source line. No page a held file projects into (`analytics.mdx`,
`data-engine.mdx`, `hook.mdx`, `driver-turso.mdx`) moved.
- **A `patch` changeset** for `@objectstack/spec` (see Changeset below).

## Census: `data/`, before and after

**Instrument.** This is the instrument of stages 1 and 2. It sends REST
`GET /repos/objectstack-ai/objectstack/issues/N` without following
redirects, for every distinct number cited in `packages/spec/src/data`.
The population is:
- the citation gate's own exported `CITATION_RE` and
`NON_CITATION_HEADS`, kept when the qualifier is none, `objectstack`,
`objectstack-ai/objectstack`, `framework`, `pre-` or `post-`;
- widened here to the capitalised spellings of those qualifiers (`Pre-`,
`POST-`, `Framework`: 7 sites, one of them dead), which stage 2's
case-sensitive set did not read;
- N of 100 or more, excluding `summon` heads.

Each site is classified by the TypeScript parser as a line comment, a
docblock, a block comment or a string.

**Controls.** The lit controls were `objectstack-ai#16862`, `objectstack-ai#16847` and `objectstack-ai#17698`. The
dead controls were `objectstack-ai#16714`, `objectstack-ai#16715` and `objectstack-ai#16697`. They were probed at
the start, after every 100 numbers and at the end. They read 24 of 24
lit (200) and 24 of 24 dead (404) over 8 checkpoints in both runs.

| reading | tree | numbers probed | 200 | 404 | 301 or other | dead
sites, all of `data/` | in scope | excluded (held files) | in-scope
lines | in-scope files | dead numbers in scope |
|---|---|---|---|---|---|---|---|---|---|---|---|
| before | base `9bf5e67af`, probed 2026-09-28T19:32Z to 19:36Z | 618 |
571 | 47 | 0 | **240** | 207 | 33 | 204 | 47 | 43 |
| after | head `96fd49caa2`, probed 2026-09-28T23:19Z to 23:23Z | 600 |
571 | 29 | 0 | **77** | 44 | 33 | 43 | 16 | 21 |

**Before, in scope, by class.** 92 non-test docblock sites and 13
non-test line comments. 16 test docblock sites and 45 test line
comments. 39 test string sites. 2 non-test string sites.

**After, in scope.** 41 string sites and 3 comment sites remain, all
three deliberate. The head probe found no number newly dead since the
base probe: the same 571 numbers answer 200.

PR objectstack-ai#20226's area table read `data` 239 at an earlier base; this census
reads 240 at `9bf5e67af`. The 33 excluded sites sit in `object.zod.ts`
(15), `analytics.zod.ts` (3), `analytics-strictness-batchd.test.ts` (2),
`analytics-date-range-two-bound-window.test.ts` (1),
`driver/turso.zod.ts` (2), `driver/turso.test.ts` (3),
`filter-subtree-provenance.ts` (3), `filter-logic-conformance.ts` (3)
and `object.form.ts` (1). `data-engine.*` and `hook.form.ts` carry none.

## Per-number table

The counts are in-scope sites and files at the base. `rewritten / left`
gives comment sites rewritten and sites left. Every anchor was read in
its diff or message, not only in its subject: it is the commit that made
the change the line now describes, and its own diff or message names the
number it replaces.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#6111` (objectui) | 1/1 | 0/1 | objectui's number, left: see
Acceptance notes |
| `objectstack-ai#6259` | 5/2 | 1/4 | `6968885ef`: retires the producer-less `batch:
'bulk'` row of `DATA_ACTION_TO_API_OPERATION` and the prose calling
`batch` a runtime action. The marker and 2 test strings stay (see
Acceptance notes) |
| `objectstack-ai#6345` | 18/5 | 17/1 | `e2798fab7`: one driver vocabulary; both boot
hosts read the shared table; `mongo` to `mongodb`; turso a builtin; the
fork-1 and fork-2 refusals |
| `objectstack-ai#6571` | 10/2 | 8/2 | `2f3e79351`: `$between` endpoints accept the
ISO/clock strings the platform produces, as a bare string (rider ①) |
| `objectstack-ai#8495` | 9/2 | 6/3 | `4bfe1a539`: refuses `${…}` placeholders in
memory `persistence.path` / `persistence.key` at publish |
| `objectstack-ai#8656` | 1/1 | 0/1 | a test title only |
| `objectstack-ai#8696` | 20/8 | 17/3 | `90a12fb18`, the card's mongodb arm: a bound
secret rides beside an unmodified url as MongoClient `auth`. Its own
pins carry the multi-host form `new URL()` cannot parse and the bound
secret outranking `options.auth` |
| `objectstack-ai#8772` | 3/2 | 3/0 | `75b7c240a`: Direction 2 of the 2026-08-16
maintainer ruling. The builder forces `required: true` on a
`master_detail` under `controlled_by_parent`, and raw parse stays
tolerant. ADR-0055 stays cited beside it |
| `objectstack-ai#8778` | 1/1 | 1/0 | `7901b2dd2`: stamp-only
`tenancy.organizationField`, declared by `sys_api_key` |
| `objectstack-ai#8794` | 2/1 | 2/0 | `1850ebbb0`: corrects the reuse-safety claim on
the filter-subtree mark from the survey's measurement, and routes a
mechanism change to a spec-seat ruling (stage 1's anchor too) |
| `objectstack-ai#8836` | 2/1 | 2/0 | `1850ebbb0`: the same commit, which pins the
invariant (one line carries both numbers) |
| `objectstack-ai#8873` | 6/3 | 6/0 | `096106522`: a bound `credentialsRef` reaches
the postgres server on the DSN branch. Its diff records that `pg` sends
a password only when the server asks |
| `objectstack-ai#8874` | 1/1 | 1/0 | `d70428ae7`: a declared mysql `ssl` reaches
`mysql2` as its own TLS options object, because `mysql2` rejects a bare
boolean |
| `objectstack-ai#8876` | 9/5 | 6/3 | `d634e665b`: exports `urlUserinfoUsername`, and
its diff states the asymmetry that a username is not credential material
|
| `objectstack-ai#9040` | 20/6 | 14/6 | `24206416a`: refuses a credential in the mongo
options passthrough at publish, and redacts the passthrough secret paths
on read |
| `objectstack-ai#9041` | 22/2 | 17/5 | `d491625c1`: refuses a bound `credentialsRef`
with a user-less mongo `config.url`, with the triage's fences |
| `objectstack-ai#10165` | 5/1 | 1/4 | `801296050`: `ttl.onlyWhen` with the canonical
null predicate (maintainer ruling 2026-08-20, option A) |
| `objectstack-ai#10274` | 1/1 | 1/0 | `d1ba685ec`: re-measures the objectui pin
citations and gates the class |
| `objectstack-ai#10329` | 6/2 | 6/0 | `15d58dbf1`: retires the import lookup
transform's steering params (ADR-0049) |
| `objectstack-ai#10347` | 2/1 | 2/0 | `530c1df65`: the Archiver honours a declared
`ttl` (maintainer ruling 2026-08-20) |
| `objectstack-ai#10527` | 2/1 | 1/1 | `5649efbf9`: refuses a diverging retention +
ttl + archive triple at parse time |
| `objectstack-ai#11065` | 7/3 | 5/2 | `20950404c`: a boolean aggregand counts as 1 or
0 in `avg` and `sum`, the first face aligned. No commit message names
the card; this is where the number first entered the tree |
| `objectstack-ai#11195` | 3/1 | 2/1 | `b37231883`: `UserActionsConfigSchema` adopts
`group` / `hideFields` / `rowColor` |
| `objectstack-ai#11215` | 1/1 | 1/0 | `42a117b88`: documents
`NoSQLIndexSchema.unique`'s deliberate scope-vocabulary omission |
| `objectstack-ai#11350` | 1/1 | 1/0 | `ece4dad31`: records the 2026-08-23 maintainer
ruling on entry nameability (stage 1's anchor too) |
| `objectstack-ai#11408` | 2/1 | 1/1 | `f11fc61c5`: declares `editMode` (maintainer
ruling 2026-08-24) |
| `objectstack-ai#11507` | 5/2 | 5/0 | `88b9d749a`: declares `sys_activity.type` an
open, author-extensible vocabulary (maintainer ruling 2026-08-24,
direction 4) |
| `objectstack-ai#11658` | 1/1 | 1/0 | `1a6a19c31`: opens `RecordActivityProps.types`
to author-contributed kinds |
| `objectstack-ai#12380` | 4/2 | 4/0 | `4045b954d`: makes the SQLite `Field.json`
codec injective; its message carries the measured boundary |
| `objectstack-ai#12868` | 1/1 | 0/1 | a test title only. Its comment site sits in
`object.form.ts`, now held by PR objectstack-ai#20519; its deciding commit is
`c459da6bc` (see Acceptance notes) |
| `objectstack-ai#13156` | 1/1 | 1/0 | `fd289be45`: strips tracker ids from
function-declaration-built refusal prose (the card's A half) |
| `objectstack-ai#13644` | 3/2 | 2/1 | `34ce8e7db`: declares
`ctx.referentialFieldClear` on `HookContextSchema` |
| `objectstack-ai#14426` | 2/2 | 1/1 | `40a44b91b`: the undefined-comparand refusal
prescribes the null predicate by its ruled spellings, position-safe |
| `objectstack-ai#14676` | 1/1 | 1/0 | `13c48c2a5`: retires `connector.errorMapping`;
its test states the same assertion-set reasoning |
| `objectstack-ai#16126` | 2/2 | 2/0 | `859ded3ec`: refuses a whitespace-only
`reference` on lookup / master_detail |
| `objectstack-ai#16685` | 4/2 | 4/0 | `ed7243d52`: accepts boolean / toggle for sum /
avg / min / max (decision batch objectstack-ai#80) |
| `objectstack-ai#16867` | 3/2 | 2/1 | `0ee32edef`: `notNull` / `not_null` prescribe
`storage.notNull`, not `required` |
| `objectstack-ai#17014` | 3/2 | 2/1 | `80aef8032`: the one-day date-range presets
prescribe a one-day window, and the table states its end-token
convention |
| `objectstack-ai#17286` | 1/1 | 1/0 | dropped: a tracking card with no landing. The
sentence now says the card is gone and to measure `driver-memory` for
the open set |
| `objectstack-ai#17348` | 1/1 | 1/0 | `51efbf116`: pins the `driver-memory` temporal
text-operator divergence by name in that driver's conformance suite |
| `objectstack-ai#17590` | 1/1 | 1/0 | `e04a0aff2`: `$contains` on a JSON column is a
per-dialect membership test (director-seat ruling 2026-09-12) |
| `objectstack-ai#18012` | 8/3 | 7/1 | `176b03582`: `$between` requires two non-blank
endpoints (decision batch objectstack-ai#146 item 5, letter A) |
| `objectstack-ai#19377` | 6/2 | 6/0 | `a60c913de`: refuses a `{ $field }` reference
as a `$between` endpoint at the runtime filter door |

Every cited sha matches exactly one commit (`git rev-parse
--disambiguate`, count 1), and every one is an ancestor of the base
(`merge-base --is-ancestor`, exit 0). That is 38 distinct shas.

Wordings to check, each true of its commit:
- `datasource.zod.ts:352` names only the card's mongo arm (`90a12fb18`)
for "the defect class … closed", because the paragraph is about mongo.
The card's mysql arm (`72050cc47`) is not cited anywhere in this stage.
- `datasource.zod.ts:354`: 「the triage's, as commit d491625 landed
them」. `d491625c1`'s message lists the fences as "per triage".
- `filter.zod.ts:1021-1025`: the `objectstack-ai#17286` pointer becomes 「was measured
on a tracking card … That card is gone: measure `driver-memory` for the
open set, ⛔ not this text.」 The warning that this paragraph is not the
authority is kept.

## The 41 string sites left as tokens

- **Test titles and test-code strings (39 sites).**
`driver/driver-credential-refusal.test.ts` 14, `object.test.ts` 6,
`datasource-credential-redaction.test.ts` 3,
`driver/driver-placeholder-refusal.test.ts` 3, `filter.test.ts` 3,
`api-derivation.test.ts` 2 (the `split('[objectstack-ai#6259]')` literal and its
message), `field.test.ts` 2, and 1 each in `date-range-presets.test.ts`,
`driver/postgres.test.ts`, `field-rows-option-description.test.ts`,
`filter-comparand-type.test.ts`, `hook.test.ts` and
`object-strictness-batch20.test.ts`.
- **Non-test strings (2 sites).** `aggregation-conformance.ts:398` and
`:407`, the `note` of two exported `AGGREGATION_CASES` rows (`objectstack-ai#11065`,
`objectstack-ai#11151`). They ship as data. Their only readers are driver conformance
suites, which print a `note` as the assertion message when a case fails,
to a driver developer and never to a metadata author. So they are
neither comments nor form D author-shown text. This is the same
disposition stage 1 gave the two `why` strings and stage 2 the
`PROVENANCE_WAIVERS` reason.

No author-shown text in `data/` carries a dead number, so nothing here
is objectstack-ai#20233's form D.

## Mechanical guard: no code token moves

The check compares leaf tokens with comments stripped, base `9bf5e67af`
against head `96fd49caa2`. It uses the TypeScript parser's leaf tokens,
so template literals are scanned in context, and it excludes JSDoc
nodes. It ran over all 45 touched `.ts` files.

- Real run: 140,379 base tokens, **0 files with a token change** (exit
0).
- Comment-insertion control: 0 files changed, as expected (exit 0).
- Positive control (a declaration inserted into `feed.zod.ts`): 1 file
reads DIFFER (exit 1).
- Positive control (one digit changed inside the `split('[objectstack-ai#6259]')`
string in `api-derivation.test.ts`): 1 file reads DIFFER (exit 1).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/spec`
is included. It says only that the provenance comments were re-anchored.

Measured on the built package: 14 of the touched sources are
`src/**/*.zod.ts`, which `files[]` ships verbatim. The rewritten
docblocks also reach `dist`. `88b9d749a`, `e2798fab7` and `24206416a`
each appear in 1 declaration file. `24206416a` appears in 20 bundled
`.js` files and `2f3e79351` in 28. The positive control, a pre-existing
`feed.zod.ts` docblock sentence, appears in `dist/data/index.d.ts`.

## Gates (head `96fd49caa2`)

- **Citation judging pass, run as CI runs it:** `pnpm
check:issue-citations && node scripts/check-issue-citations.mjs` exits
0. The self-test passes 73 cases in 7 batteries. The live run judged 11
citations across 25 files, and all 11 resolve.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at the final head derived 108
families, and all 108 exit 0. `--ran` reports 108 run, 0 NOT MEASURED, 0
unrun, and exits 0. (`check:i18n` was derived at the earlier heads from
`object.form.ts`, and left the set when that file went back to base.)
- At an earlier head, four gates first exited 3 (PREREQUISITE NOT MET)
because the workspace was unbuilt: `check:doc-formula-expressions`,
`check:doc-security-posture`, `check:skill-examples` and
`check:docs-transcript-drift`. At the final head a full `turbo run
build` of `./packages/*` ran first (71 tasks, exit 0, under the shared
verify lock), and every gate exited 0 on its first run.
- `check:generated` was run under the lock against that build: all 15
artifacts are up to date.
- **Build, tests, typecheck and lint:**
  - `pnpm --filter @objectstack/spec build` exits 0.
- `vitest run --maxWorkers=2 src/data` in `packages/spec` at the final
head: 107 files and 3,517 tests pass (1 todo), covering every touched
test file.
- The 12 spec suites outside `src/data` that read `data/` source text
pass at the final head: 12 files, 503 tests. These are
`scripts/{file-description,root-index,skill-map-guards,strictness-ledger}.test.ts`,
`src/api/api-entry-graph.pin.test.ts`,
`src/contracts/scoped-context.test.ts`,
`src/shared/{alias-integrity,evaluated-slot-population,retired-key-migrate-sentence}.test.ts`,
`src/system/constants/platform-object-names.test.ts`,
`src/type-alias-convention.pin.test.ts` and `src/ui/dashboard.test.ts`.
- `pnpm --filter @objectstack/spec typecheck` at the final head exits 0,
including `check:test-typecheck` (53 files, 251 errors, 138 pinned
signatures held).
- Lint, as a proven narrowing at the final head: `eslint
--no-inline-config --format json` over the 45 touched `.ts` files gives
45 files, 0 errors and 0 warnings. All 45 are in eslint's own population
(`isPathIgnored` is false for each). `eslint.config.mjs` never enables
type-aware linting (no `parserOptions.project`, which its own line 328
states), so a comment edit here cannot move the verdict on any untouched
file. The repo-wide `pnpm lint` is CI's run.

## Acceptance notes

- **The `[objectstack-ai#6259]` marker.** `api-derivation.test.ts:236` splits
`DATA_ACTION_TO_API_OPERATION`'s TSDoc on the literal `[objectstack-ai#6259]`, and a
test string may not change here. So the marker line
`api-derivation.ts:163` is byte-identical to the base, and the test
comment at `:232` that names the marker stays too. The sentence's
deciding commit sits on the next line instead: 「(both by commit
6968885)」. A first attempt wrote the commit onto the marker line
itself. The diff-scoped `check-issue-citations` then read the kept
`objectstack-ai#6259` as an added citation and exited 1, so it was moved one line down
(commit `b93f08f8d0`).
- **objectui's `objectstack-ai#6111`.** `field.zod.ts:370` reads 「objectui#6110 +
objectstack-ai#6111 (section)」. The qualifier covers only the first number, so the
citation grammar reads `objectstack-ai#6111` as this repository's (404 here). It is
objectui's number: its introducing commit `f887e5249` writes
`(objectui#6111)` in the same diff, and `objectstack-ai/objectui`
answers REST 200 for objectstack-ai#6111 to this session (and for objectstack-ai#6110 and objectstack-ai#10264).
objectui has no `refs/pull/6111/head`, so it is an issue there, not a
PR. The line is left unchanged. This is objectstack-ai#20330's grammar family, the
same as stage 2's `objectui PR objectstack-ai#10264`, and it is noted there, not
filed.
- **Capitalised qualifiers.** `CITATION_RE` classes `Pre-#N`, `POST-#N`
and `Framework#N` (7 sites in `data/`) as cross-repo and never judges
them. This census read them as this repository's. One was dead and is
rewritten here (`object.test.ts:223`, `POST-objectstack-ai#10347`). This is the same
objectstack-ai#20330 family as stage 1's `pre-` / `post-` finding.
- **Four files held after the claim.** Each joined the exclusions and
went back to the base bytes (hypothesis 2 of the dispatch). Each PR's
hunks were disjoint from this PR's lines, but the dispatch's rule is
file-level.
- `driver/turso.test.ts`: PR objectstack-ai#20504 (objectstack-ai#20437's) opened at
2026-09-28T20:08Z and edits it. Its two comment sites (`:4`, `:58`, both
`objectstack-ai#6345`) went back to blob `7fe99ebf9` in commit `86463ed0a1`. A
no-driver `merge-tree` of that head with PR objectstack-ai#20504's head `5dfa45e9f`
exits 0.
- `object.form.ts`: PR objectstack-ai#20519 (objectstack-ai#20432's) opened at 21:55Z and edits it.
Its one comment site (`:256`, `objectstack-ai#12868`, whose deciding commit is
`c459da6bc`) went back to blob `60713e06f` in commit `3479600dda`.
- `object.zod.ts`: PR objectstack-ai#20521 (objectstack-ai#20494's) opened at 22:10Z and edits one
line at `:2123`. Its 15 comment sites (`objectstack-ai#8772`, `objectstack-ai#10165`, `objectstack-ai#10347`,
`objectstack-ai#10527`, `objectstack-ai#11195`, `objectstack-ai#11408`, `objectstack-ai#13608`) went back to blob `befde04ca` in
commit `96fd49caa2`. Their deciding commits are `75b7c240a`,
`801296050`, `530c1df65`, `5649efbf9`, `b37231883`, `f11fc61c5` and
`fc9ba76a5`, all read for this stage.
- `filter-logic-conformance.ts`: PR objectstack-ai#20523 (objectstack-ai#20444's) opened at 22:39Z.
Its 3 comment sites (`objectstack-ai#13195`) went back to blob `c9b32acba` in the same
commit. Their deciding commit is `9dac1ae01`, with `PR objectstack-ai#13529` as the
link.
- **What stays for later stages.**
- The 33 dead sites in the held files listed above. The later stage can
reuse the deciding commits named for them here.
  - The 41 string sites and the 3 deliberate comment sites above.
- The `data/` numbers that also appear in
`packages/spec/src/migrations/**`. Those are objectstack-ai#20233's form D, or the
migrations stage.
- **The rung.** Several anchored changes also have ADR-0087 entries in
`packages/spec/src/migrations`. Examples are
`cbp-master-detail-required-forced` for objectstack-ai#8772,
`filter-between-blank-endpoint-refused` for objectstack-ai#18012, the `datasource-*`
entries for objectstack-ai#9040, objectstack-ai#9041 and objectstack-ai#8873, and the
`mapping-lookup-params-removed` conversion for objectstack-ai#10329. This PR takes the
commit rung, as stages 1 and 2 did, so it is precedent-consistent. The
D3 id is the more durable in-repo record, if the ruling's first rung is
later read to include those entries.
- **The citation gate's reach.** It defers `packages/**/*.test.ts`, so
20 of the 45 touched `.ts` files never enter its judging population. The
added-minus-removed count over the whole diff covers them: 0 numbers
added.
- **Base.** The branch is 22 commits behind `origin/main` (`1378ec7c0c`,
read at 2026-09-29T00:18Z). Four of those commits touch `data/`, all in
excluded files: objectstack-ai#20475's `hook.form.ts`, objectstack-ai#20487's `data-engine.*`, and,
since this stage excluded them, PR objectstack-ai#20521's `object.zod.ts`
(`9e1689f8e2`) and objectstack-ai#20444's `filter-logic-conformance.ts`
(`fb386074f5`). None touches a file in this diff, and a no-driver
`merge-tree` of the head onto `1378ec7c0c` exits 0. So there was no
merge. The open-PR file lists were re-read at 00:18Z: 11 open PRs, none
touching a file in this diff.

---
_Generated by [Claude
Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] 承重出处该用什么形式:同一周已合并 PR 编号有 11.7–15.1% 今天已 404,而本仓源码正以它作凭据

2 participants