docs(spec): re-anchor the dead tracker citations left in data/ to the commits that decided them (stage 4) - #20548
Conversation
… commits that decided them (stage 4) The six data/ files stage 3 left because open PRs held them are free again. Three of them carry comment or docblock sites that cited a tracker number answering 404: object.zod.ts, filter-logic-conformance.ts and object.form.ts. Each such line now cites the commit in this repository's history that decided what it describes, with a PR number only beside it as a link. data-engine.zod.ts, data-engine.test.ts and hook.form.ts carry no dead citation. Comment-only: every file keeps its line count and no code token or test string moves. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
…horing object.zod.ts ships verbatim through files[] (src/**/*.zod.ts), so the rewritten comments publish bytes and @objectstack/spec takes a patch changeset. No surface moves. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 5 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 32efa96a1663277a26465db97c03c318c4937197 && git checkout 32efa96a1663277a26465db97c03c318c4937197
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ba5927f714af7516105706b36a05cedf34d5fa1b 53c9070dfdc8165eed3e552a612fe42e05336c19 && git checkout -B drift-repro ba5927f714af7516105706b36a05cedf34d5fa1b && git merge --no-ff 53c9070dfdc8165eed3e552a612fe42e05336c19
node scripts/docs-audit/affected-docs.mjs --json ba5927f714af7516105706b36a05cedf34d5fa1b
|
Contract reviewServed-tier: Read: card #20234 (body; every comment: triage ① Derived judgments(a) Scope and file surface: right. 4 files: (b) Comment-only, no code token or test string moves: right. (c) Census: right. The 133 distinct numbers (100 or more) cited in the six files at head were probed with REST (d) Anchor truth: right, 9 of 9. Nine sha-shaped tokens stand on added lines and none on removed lines;
(e) Form C: right. Every rewrite leads with the commit ( (f) Projection: right. No page under ② Semver level
③ Boundary flagsBlocking: none. Dev deviations, each answered:
Non-blocking: (1) Rung: two of the anchored changes also have ADR-0087 entries in Escalated: none. CI at this head, the judging read at 2026-09-29T02:15:17Z: 33 check-runs, 23 success, 3 skipped ( Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #20234
Clause-②: no
What changed
This is stage 4 of the staged sweep: the
data/remainder. It covers the sixpackages/spec/src/data/files stage 3 (PR #20533, landed03b19d9cfd) left out because an open PR held them, and nothing else. They areobject.zod.ts,filter-logic-conformance.ts,object.form.ts,data-engine.zod.ts,data-engine.test.tsandhook.form.ts. Later stages cover the other areas, so this PR saysPart of.The census below measured all six. Three of them carry comment or docblock sites that cite a tracker number answering 404.
data-engine.zod.ts,data-engine.test.tsandhook.form.tscarry none, so they are not in the diff.Every such site has been rewritten in ruling C+D's form C (comment 5749154545 on #19123). That is 19 sites on 19 lines in 3 files, covering 9 numbers. Each rewritten line now cites the commit in
origin/mainhistory that decided what the line describes, and it says in its own words what that commit decided. Where a PR number was already on the line (PR #13529), it stays beside the commit as the link.No ADR or ruling-record file in
docs/adr/orscripts/adr-anchors/records the decision behind any of the 9 numbers: a search for each number, with and without#, finds nothing there. So every anchor is a commit: 9 distinct shas. Stage 3 had already read these commits and recorded them in PR #20533's body. They were not copied from there. Each one was re-read against the current line it anchors: its own message or diff names the number it replaces, and it made the change the line describes.object.zod.tsandfilter-logic-conformance.tsmoved onmainafter stage 3 read them (PRs #20521 and #20523). Each site was therefore re-read at this base,03b19d9cfd.Only comments changed. Every source file keeps its line count (20 lines out, 20 in, over 3 files), so no line citation into these files moves. One of the 20 lines held no dead citation:
filter-logic-conformance.ts:249, the first half of a sentence reflowed onto:250. No code token moves (see the guard below).No tracker number is added. Every tracker number on an added line was already in the hunk it replaces.
PR #13529stands on three added lines, and on the three removed lines of the same hunks. It is the link beside commit9dac1ae01, which stage 3 recorded the same way.No reference page under
content/docs/references/moved: none of the rewritten docblocks projects into one (check:docsat the head:226 generated files in sync). The PR adds onepatchchangeset for@objectstack/spec(see Changeset below).Census: the six files, before and after
Instrument. This is the instrument of stages 1 to 3. It sends REST
GET /repos/objectstack-ai/objectstack/issues/Nwithout following redirects, for every distinct number cited inpackages/spec/src/data. The population is:CITATION_REandNON_CITATION_HEADS, kept when the qualifier is none,objectstack,objectstack-ai/objectstack,framework,pre-orpost-;Pre-,POST-andFramework, as in stage 3;summonheads.Each site is classified by the TypeScript parser as a line comment, a docblock, a block comment or a string.
Two cross-checks close the population. First, a raw
#Ncount in each of the six files equals the census rows plus the cross-repo rows in five files. In the other two it is one higher, and the extra is a second number after a slash inside a string (#5322/#5134in anote,#6262/#6433in a test title). Both answer 200. Second, no spelled citation (issue N,PR N,card N) occurs in any of the six.Controls. The lit controls were
#16862,#16847and#17698. The dead controls were#16714,#16715and#16697. They were probed at the start, after every 100 numbers and at the end: 24 of 24 lit (200) and 24 of 24 dead (404) over 8 checkpoints in the base run, and 21 of 21 lit and 21 of 21 dead over 7 checkpoints in the head run.data/03b19d9cfd, probed 2026-09-29T01:11:59Z to 01:15:49Z53c9070dfd, probed 2026-09-29T01:25:55Z to 01:29:35ZThe head probe found no number newly dead since the base probe: the same 572 numbers answer 200. The base reading of 77 equals stage 3's after reading at
96fd49caa2.Per file. Cited sites here are every in-repo citation the population reads, live or dead.
object.zod.tsfilter-logic-conformance.tsobject.form.tsdata-engine.zod.tsdata-engine.test.tshook.form.tsNone of the 19 sites is a string, so this stage leaves no string token behind.
Per-number table
#8772object.zod.ts:2718,:2731,:2744,:291075b7c240a: Direction 2 of the 2026-08-16 maintainer ruling.ObjectSchema.create()forcesrequired: trueon amaster_detailreference undercontrolled_by_parentand refuses an explicitrequired: false. Raw parse stays tolerant, and runtime tolerance is the ruling's other half. Its changeset records the measurement that only the security gate closed that shape while the declaration surface accepted it (:2731). ADR-0055 stays cited beside it. It is the same anchor stage 3 gaveobject.test.ts#10165object.zod.ts:818,:1036801296050:ttl.onlyWhenwith the canonical null predicate (maintainer ruling 2026-08-20, option A). One sharedonlyWhenunion, and both ofretention.onlyWhen's conflicts mirrored. Its diff wrote both[#10165]blocks#10347object.zod.ts:1006,:1042,:1049530c1df65: the Archiver honours a declaredttl. It selects by the ttl cutoff onttl.fieldwhenttlis declared, and bycreated_at/archive.afterotherwise (maintainer ruling 2026-08-20)#10527object.zod.ts:10055649efbf9: refuses a diverging retention + ttl + archive triple at parse time. Its diff wrote this very paragraph#11195object.zod.ts:1791b37231883:UserActionsConfigSchemaadoptsgroup/hideFields/rowColor(the "last three" the line names)#11408object.zod.ts:2189f11fc61c5: declareseditModeon the object document (maintainer ruling 2026-08-24, the#10144declare-or-rule-out family, which stays cited)#13608object.zod.ts:2317,:2354,:2366fc9ba76a5:publicSharing.eligibilityis held at redemption, not only at mint, fail-closed, with the undifferentiatednullrefusal. Its changeset heads with #13608. It is the same anchor stage 1 gavecontracts/share-link-service.ts#13195filter-logic-conformance.ts:190,:250,:5259dac1ae01, PR #13529's squash commit, which stays as the link:$existsmeans has-a-value on driver-memory's live mingo path, its analytics face and driver-mongodb'stranslateFilter(the "last three key-presence exits")#12868object.form.ts:256c459da6bc: narrows the per-optiondefaultkey out of the form-view options vocabulary, which offered a key nothing on that surface read. Commite808890958, which wrote this line, names #12868 as the same offer-vs-door classThe shas were checked at the base and again at
origin/main288611e3e5. Every one matches exactly one commit (git rev-parse --disambiguate, count 1). Every one is an ancestor (git merge-base --is-ancestor, exit 0 for 9 of 9). The control lege9584681a4also exits 0, and the repository is not shallow. For each commit, a grep of its own message or diff finds the number it replaces. Seven of the nine name it in the message.fc9ba76a5names it in its diff (20 lines, including its changeset heading), and so doesc459da6bc(8 lines, including its changeset heading).Wordings to check, each true of its commit:
object.zod.ts:2731now reads 「closes that shape, and commit 75b7c24 records that the declaration and the enforcement disagree」. The measurement was the card's. The commit's changeset records it: "only the security gate closed that shape while the declaration surface accepted it".object.zod.ts:2189reads 「Declared here by commit f11fc61's maintainer ruling」, and:2744reads 「the other half of commit 75b7c24's ruling」. This is stage 3's wording for the same relation (object.test.ts, 「the other half of commit 75b7c24's ruling」): the commit that landed the ruling and quotes it.object.zod.ts:1049reads 「That is the whole of what [commit 530c1df] changed here」. Commit52db1d1f2awrote the paragraph.530c1df65is the change it describes.Mechanical guard: no code token moves
The check compares leaf tokens with comments stripped, base
03b19d9cfdagainst head53c9070dfd. It uses the TypeScript parser's leaf tokens (TypeScript from the head's lockfile), so template literals are scanned in context, and it excludes JSDoc nodes. It ran over all 3 touched.tsfiles. It is the stage-3 instrument, unchanged.object.form.ts): 0 files changed, as expected (exit 0).object.zod.ts): 1 file reads DIFFER at token 1629 (exit 1).#5322/#5134notestring infilter-logic-conformance.ts): 1 file reads DIFFER at token 889 (exit 1).Line balance:
object.zod.ts+15 / -15,filter-logic-conformance.ts+4 / -4,object.form.ts+1 / -1. Line counts are equal at base and head: 3,240, 621 and 751.Changeset
This change ships bytes, so a
patchchangeset for@objectstack/specis included. It says only that the provenance comments were re-anchored.Clause-②: no: no export, key, value or type moves (the guard above).Measured on the head's built package:
object.zod.tsissrc/**/*.zod.ts, whichfiles[]ships verbatim. The rewritten comments also reachdist:9dac1ae01appears indist/data/index.d.ts(thefilter-logic-conformance.tsdocblock) and in 4 bundled.jsfiles;fc9ba76a5,f11fc61c5andb37231883each appear in 22 bundled.jsfiles, andc459da6bcin 12;object.zod.tssentence 「Fail-CLOSED at both points」, appears in 11 bundled.jsfiles.Gates (head
53c9070dfd)pnpm check:issue-citations && node scripts/check-issue-citations.mjsexits 0. The self-test passes 73 cases in 7 batteries. The live run judged 6 citations across 3 files: 3 resolve (#9138twice,#11410) and 3 resolve as a pull request (#13529, the link).pnpm check:doc-authoringexits 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat the head derived 79 families, and all 79 exit 0.--ranreports 79 run, 0 NOT MEASURED, 0 unrun, and exits 0. A fullturbo run buildof./packages/*ran first, under the shared verify lock: 71 of 71 tasks, VERDICT command-exit 0. So no gate met an unbuilt prerequisite.pnpm --filter @objectstack/spec run check:generated: under the lock against that build,All 15 generated artifacts are up to date, VERDICT command-exit 0.pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/dataunder the lock: Test Files 107 passed (107), Tests 3527 passed, 1 todo (3528), VERDICT command-exit 0. It covers every test indata/, among themobject.test.ts, which reads these schemas.src/datathat read the touched files' source text or pin their line numbers, under the lock: Test Files 13 passed (13), Tests 544 passed (544). They are stage 3's 12 (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,src/ui/dashboard.test.ts) plussrc/shared/union-author-message-pins.test.ts, which pinsdata/object.zod.ts:855.pnpm --filter @objectstack/spec typecheckunder the lock exits 0, includingcheck:test-typecheck(53 files, 251 errors, 138 pinned signatures held).eslint --no-inline-config --format jsonover the 3 touched.tsfiles gives 3 files, 0 errors and 0 warnings. All 3 are in eslint's own population (isPathIgnoredis false for each).eslint.config.mjsnever enables type-aware linting (noparserOptions.project, which its own line 328 states), so a comment edit here cannot move the verdict on any untouched file. The repo-widepnpm lintis CI's run.Acceptance notes
03b19d9cfd, stage 3's landing.origin/mainthen moved two commits (05077d4c26, PR fix(spec): shard the generated liveness and strictness counts so PRs moving different units stop conflicting on a committed total #20532, and288611e3e5, PR fix(spec): os migrate meta guidance for the rest-*, analytics-*, view-*, package-*, object-*, sharing-*, audit-*, flow-* and http-* migration entries states each lesson in words, not tracker numbers (stage 6) #20536), and neither touchesdata/.dispatch-gatesflagged its derivation as stale becausescripts/regen-artifacts.mjshad moved, soorigin/mainwas merged in (53c9070dfd, a clean merge with no driver-deferred path) before the gates ran. The PR's delta againstorigin/mainis exactly its 4 files.origin/mainhas since moved two more commits:7e36a3cd7c(PR spec(object-timeline): items describe names objectui as the item-shape owner #20531) andba5927f714(PR feat(spec,cli)!: one stack authoring shape — os validate / os build refuse a default export defineStack did not build #20460). Neither touchesdata/or anything the gate derivation reads, and a re-derivation prints the same 79 commands. A no-drivermerge-treeof the head ontoba5927f714, from a bare shared clone, exits 0. So there is no second merge.data/files open PRs touch are feat(spec)!: retire the inner name on cube measures and dimensions — the record key is the member's name (#20300) #20458'sanalytics*files, fix(spec,driver-turso)!: refuse a forced mode replica with no syncUrl at authoring and at construction (#20437) #20504'sdriver/turso.*, and fix(spec)!: a boolean, a Date or an array compared against a number field is refused like a non-numeric string (#20502) #20545'sfilter-number-comparand-declared-type.*, which is disjoint. Since the claim, PR feat(spec,cli)!: one stack authoring shape — os validate / os build refuse a default export defineStack did not build #20460 has landed (ba5927f714) without touchingfilter-subtree-provenance.ts. That file's 3 dead sites are outside this claim's fence, so they are left for a later stage.packages/spec/src/migrations:cbp-master-detail-required-forcedfor Acontrolled_by_parentobject may declare its master reference withoutrequired, so the master-access guard is the only thing preventing an unreachable orphan detail row #8772, andform-view-option-default-retiredfor Narrow the per-optiondefaultkey OUT of the form-view options vocabulary — the object-field face keeps it (enforced, #7246), the form-view face parses-and-ignores it (ruled on objectui#6263) #12868. The second entry's own header names commitc459da6bc. This PR takes the commit rung, as stages 1 to 3 did. The D3 id is the more durable in-repo record, if the ruling's first rung is later read to include those entries.data/after this stage: 58 dead sites.analytics.zod.ts,analytics-strictness-batchd.test.tsandanalytics-date-range-two-bound-window.test.tshold 5 (spec(analytics): retire the innernameon cube measures and dimensions; the record key is the identity (2 keys) #20300, PR feat(spec)!: retire the inner name on cube measures and dimensions — the record key is the member's name (#20300) #20458).driver/turso.zod.tsanddriver/turso.test.tshold 4 (turso: a datasource config withmode: 'replica'on afile:url and nosyncUrlis accepted at authoring and at construction, then runs as a plain local database that never syncs #20437, PR fix(spec,driver-turso)!: refuse a forced mode replica with no syncUrl at authoring and at construction (#20437) #20504).filter-subtree-provenance.tsholds 3. It was held by [finding]os validateruns only the stack schema parse, so a config that exports a plain object (nodefineStack()call) skips every defineStack cross-field refusal and passes #20367 and is now free (see above).[#6259]marker atapi-derivation.ts:163, the test comment atapi-derivation.test.ts:232that names it, andfield.zod.ts:370, whose#6111is objectui's number.AGGREGATION_CASESnote strings inaggregation-conformance.ts(:398,:407,avgover a boolean field returnsnullon driver-memory and a number on sqlite — same rows, same dataset, same executor #11065), which aggregatesum/avgover 3+ fractional addends: SQLite native adds with compensation (0.1+0.2+0.3 = 0.6), every other face and the rows path naively (0.6000000000000001), sohaving $eq 0.6keeps the group on SQLite native only #20489's claim holds.data/, the card's other remaining items are unchanged: the migrations and ui areas, theliveness/**notes, thewhystrings, thePROVENANCE_WAIVERSreason, andrest-server.zod.ts.packages/**/*.test.ts. No test file is touched here, so all 3 touched files are in its judging population.Generated by Claude Code