docs(service-storage): re-anchor the dead tracker citations to the commits that decided them - #20708
Conversation
…mmits that decided them 42 comment and docblock sites under packages/services/service-storage/src cited tracker numbers that answer 404. Each now cites the commit in this repository's history that decided what the line describes, and says in its own words what that commit decided: the sys_attachment beforeUpdate gate (da891e0), the full-envelope ruling on the sharing contract (aa4b90d), batched held-file hydration (c3c72a4), the stamp-only organizationField scope pin (7901b2d), the source-hashes provenance companion (09b4f4e), the update/delete doors scoped to the acting organization (f087c37) and the loud permission-store outage (6a180e4). Comments only: every file keeps its line count and no code token moves. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
…e comments The rewritten docblocks and inline comments ship in all four dist entry files, so the package publishes changed bytes. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 7 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 04358c57f8a4227618405fc6769a6dcf753d2091 && git checkout 04358c57f8a4227618405fc6769a6dcf753d2091
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin defc7f7b504e22b9e2c12a5374ebb255efe9b7da 09d2ecc96ad65f66dafc6fa91311fce800e81d2d && git checkout -B drift-repro defc7f7b504e22b9e2c12a5374ebb255efe9b7da && git merge --no-ff 09d2ecc96ad65f66dafc6fa91311fce800e81d2d
node scripts/docs-audit/affected-docs.mjs --json defc7f7b504e22b9e2c12a5374ebb255efe9b7da
|
Contract reviewServed-tier: ① Derived judgmentsRead against
② Semver level
③ Boundary flagsThe dev report (
Two readings that are not flags on this PR: the Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #20596
Clause-②: no
What changed
This is the sixth stage of the
domain:serviceslane of the dead-citation sweep. It coverspackages/services/service-storage/src/**and nothing else. By the seat's census at the claim (5896394242), it is the largest package in the lane that no in-flight work holds. Later stages cover the other packages, so this PR saysPart ofand the card stays open.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 #19123), by the method of stages 1 to 5 (PR #20609 as
422db788a, PR #20626 asb80ab579d, PR #20634 as4d04b6be3, PR #20658 as9a4b2bb38, PR #20693 as0e9ad74fb). That is 42 sites on 41 lines in 15 files, covering 8 numbers:Each rewritten line now cites the commit in
origin/mainhistory that decided what the line describes, and says in its own words what was decided: 7 distinct shas. No number in this package has an ADR or ruling record of its own in the repository (a grep ofdocs/adr/for all 8 finds none, and the repository keeps no other ruling-record file for them), so every anchor is a commit, per ruling C's order. No number was dropped.Only comments changed. Every touched source file keeps its line count (43 lines out, 43 in, over 15 files), so no line citation into these files moves. 2 of those 43 lines hold no dead citation; they are reflow, listed under Wordings below. No code token moves (see the guard below).
No citation number is added. Every tracker number on an added line was already on the line it replaces:
#12069(translations/index.ts:29),#10246(storage-service-plugin.ts:392) and the cross-repocloud#1395(backfill-sys-file-organizations.ts:86). Over the whole diff, added minus removed is 0 or negative for every number, and no number is new to the diff. No PR number stands on an added line.Eleven dead sites are left on purpose, all of them test titles (see the list below).
One more file: a
patchchangeset for@objectstack/service-storage, because the rewritten docblocks and inline comments ship (see Changeset below).Census:
service-storage, before and afterInstrument (A1). The gate's own
node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count below is itsallocated-but-absentfindings underpackages/services/service-storage/. Each run counts as a reading only because its board frontier equals the newest issue number, read by a separate request just before and just after the run.allocated-but-absent31ed06763, run 2026-09-29T18:42:47Z to 18:46:12Z5db5155a2, run 18:55:34Z to 18:58:50ZThe before count matches the seat's census at the claim (27 sites in 8 files, at
6bff748b). The whole-repo drop is 27, exactly this diff's census sites. Theresolvestally is 32,967 in both runs, andresolves-as-pull-request(1,984) andcross-repo-unjudged(994) did not move either. The after run was taken on5db5155a2; the head09d2ecc96adds only the changeset. No run was truncated or discarded: both enumerations read 186 pages at the newest frontier.Supplementary instrument, the whole scope. The census does not read test files or strings, and this stage's scope includes test comments. So a second reading runs the gate's own exported
extractCitations(whole-file and comment-prose projections) andnamesThisRepositoryover every.tsfile underservice-storage/src(71 files). It takes its verdicts from the before census's own board reading rather than from a second enumeration: a number is dead when that census reported itallocated-but-absent, and alive when that census judged it on this board anywhere (its--listextraction, 4,943 numbers) and did not report it. The three numbers the census never saw, because they stand only in test files (#13996,#15607,#17571), were read one by one on the issues endpoint, and each answers 200.31ed067635db5155a2Its src-comment column equals the census's 27, which is the control on the second instrument. The 554 live citations and the 1 cross-repo citation are the same in both readings, and the drop of 42 citations is exactly the rewritten sites. A third, raw reading (every
#followed by 2 to 6 digits, whatever surrounds it) finds 53 dead occurrences before and 11 after, and its residue equals the gate's residue site for site.Per-number table
Sites and files count every dead occurrence in scope at the base (comments and strings, tests included).
rewritten / leftcounts the sites rewritten and the sites left. Each anchor was read in its message and diff, not only its subject, andgit blameat the base puts each rewritten line in that commit or in a later one that applied it.#13178f087c376f: thesys_file/sys_upload_sessionupdate and delete doors take the acting organization and scope the statement to it (they stamp nothing), and the upload routes bind the session they had resolved and discarded. New to the sweep#132796a180e42d: a failed permission-store read raisesAuthzStoreUnavailableError(503) instead of reading as zero grants, and the transports' fail-closed nets, this package's file-read authorizer among them, re-raise it. The anchor of stages 2 and 5 and of the rest, runtime and types stages#10091da891e0ef:sys_attachmentbeforeUpdategated by the uploader-or-parent-editor rule, the attach rule on a re-point, and the update-verb refusal of an unscoped multi-update. New to the sweep#11427c3c72a4bc: record file-field hydration asks the reap guard's held-file question, through the batchedfindHeldFilesthis package adds, so hydration and the download path agree about a tombstonedsys_file. Its message ends with a reference to#11427. New to the sweep#6206aa4b90d9a: the full-envelope ruling applied to the sharing contract;ISharingServicetakes the wholeExecutionContext, and its docblock says callers "MUST NOT rebuild a subset of it". Stage 2's anchor, named there as the full-envelope ruling#6523aa4b90d9a: the same commit, which was#6523's change (its subject names it). Stage 2's and the spec stage's anchor#87787901b2dd2: stamp-onlytenancy.organizationField, with its consumers scope-pinned by the maintainer's ruling (the pin text is in its diff). The spec andplugin-securitystages' anchor#1167109b4f4e4e: the source-hashes provenance companion. The identicaltranslations/index.tsline inservice-messaging,plugin-sharingandplugin-securityalready cites itEvery cited sha matches exactly one commit (
git rev-parse --disambiguate, count 1 for each of the 7), and every one is an ancestor of the base (merge-base --is-ancestor, exit 0 for all 7; the history is complete,--is-shallow-repositoryfalse, 15,120 commits). Each of the 8 numbers answers 404 on the issues endpoint, read one by one before the rewrite.Wordings to check
attachment-access-hooks.ts:127and:129. 「what the 同族第三处组装:share-link 路由把授权信封裁成 4 个字段后直接当 enforcement context 喂给 engine.find ——group租户姿态下 Layer 0 墙恒判否 #6206 ruling requires … (finding:SharingExecutionContext是同族第四个窄 enforcement 契约类型(sharing / approval / report 三个服务共用),#6206 裁决的「不留 per-site 子集」默认尚未覆盖它 #6523)」 became 「what the full-envelope ruling requires … (commit aa4b90d)」. The quoted words 「MUST NOT rebuild a subset of it」 are theISharingServicedocblock thataa4b90d9awrote, so the commit sits beside the quotation. The same form atattachment-access-hooks.test.ts:766.attachment-access-hooks.test.ts:914-916. 「the finding:SharingExecutionContext是同族第四个窄 enforcement 契约类型(sharing / approval / report 三个服务共用),#6206 裁决的「不留 per-site 子集」默认尚未覆盖它 #6523 contract's unit is the envelope, and 同族第三处组装:share-link 路由把授权信封裁成 4 个字段后直接当 enforcement context 喂给 engine.find ——group租户姿态下 Layer 0 墙恒判否 #6206 forbids rebuilding a subset of it」 became 「the contract's unit is the envelope (commit aa4b90d), and the full-envelope ruling forbids rebuilding a subset of it」 (1 reflow line,:916).attachment-access-hooks.test.ts:621. 「sys_attachmenthas nobeforeUpdateauthorization guard at all — insert and delete are gated, update is not (the comment kit it was derived from gates all three) #10091 through the WIRED engine」 became 「Commit da891e0's gate through the WIRED engine」.storage-routes.ts:201,storage-service-plugin.ts:1057,file-read-tenancy-posture-admission.test.ts:582andstorage-routes.authz-outage-relay.test.ts:16. 「the confusion [finding] a permission-store read failure resolves as an AUTHENTICATED caller holding ZERO capabilities — the package door answers 403 FORBIDDEN, byte-identical to a genuine capability denial #13279 exists to prevent」 became 「the confusion commit 6a180e4 was made to prevent」: an outage answered as a capability denial is what that commit's message says it removes.storage-service-plugin.ts:1048and:1149. 「the [finding] a permission-store read failure resolves as an AUTHENTICATED caller holding ZERO capabilities — the package door answers 403 FORBIDDEN, byte-identical to a genuine capability denial #13279 relay that block already runs」 became 「the relay that block has run since commit 6a180e4」, and 「takes the [finding] a permission-store read failure resolves as an AUTHENTICATED caller holding ZERO capabilities — the package door answers 403 FORBIDDEN, byte-identical to a genuine capability denial #13279 relay in」 became 「takes the relay (commit 6a180e4) in」. The re-raise in thatcatch(:1229) is in6a180e42d's diff.storage-service-plugin.ts:1058-1059. 「it had swallowed the [finding] a permission-store read failure resolves as an AUTHENTICATED caller holding ZERO capabilities — the package door answers 403 FORBIDDEN, byte-identical to a genuine capability denial #13279 permission-store outage at this door since that card landed」 became 「it had swallowed the branded permission-store outage at this door since commit 6a180e4 landed」: 「that card」 lost its referent with the number (1 reflow line,:1059).file-reference-lifecycle.ts:111. 「the update/delete halves tenant-audit: the "write without tenantId" signal is a throttled log warn gated on multi-tenant posture, so it cannot fire in any environment where code is exercised #13178)」 became 「the update/delete halves in commit f087c37)」, beside the live#12745and#12928.tombstone-hydration-download-agreement.test.ts:320. 「the divergence finding(objectql): record file-field hydration still drops any non-committedsys_file, so it and the download path now answer the tombstone question differently #11427 fixes」 became 「the divergence commit c3c72a4 fixed」.backfill-sys-file-organizations.ts:86. 「scope-pinned by the spec: audit stamping needs a read-neutral organization declaration —tenancy.tenantFieldcannot servesys_api_keywithout walling the credential table (#8707 remainder) #8778 ruling (widened by name on cloud#1395)」 became 「scope-pinned by its ruling (commit 7901b2d; widened by name on cloud#1395)」. 「its」 is the key's own ruling, which7901b2dd2carried out and recorded as the pin; the widening is the cross-repo reference that was already there.attachment-access-hooks.test.ts:916,storage-service-plugin.ts:1059.The 11 sites left
describe/ittitles, which are string tokens, left as stages 1 to 5 left theirs:attachment-access-hooks.test.ts:232,:314,:640,:932(#10091);tenant-audit-update-delete-half-repairs.test.ts:151,:224,:345,:552,:664(#13178);tombstone-hydration-download-agreement.test.ts:148,:326(#11427).*.source-hashes.generated.tsheaders are untouched and carry none. The verbatim maintainer quotations in scope (5 lines: 「同意」 three times, 「12745 A回,其他同意。」 and 「批 Remove explicit pnpm version from workflows to fix version conflict #7 同意」) carry no dead number and are untouched.Mechanical guard: no code token moves
The guard compares the TypeScript parser's leaf nodes, with comments as trivia and JSDoc nodes never visited, base
31ed06763against head. Template literals are therefore read in context. It ran over all 15 touched.tsfiles.storage-routes.ts(Bound, not discardedtoBound and not discarded): 0 files changed, as expected (exit 0).storage-routes.ts(const { fileId, eTag } = req.body ?? {};given a trailing?? undefined): DIFFER (exit 1).tombstone-hydration-download-agreement.test.ts:148): DIFFER (exit 1).Every mutation went through
scripts/ablation-replace.mjs, and each landed (anchor 1 to 0, blob changed). Each restore was proven byte-identical to the HEAD blob (44ecc8e64ae0,ee84cf718a6f), withgit diff HEADempty and a clean tree afterwards.Changeset
This change ships bytes, so a
patchchangeset for@objectstack/service-storage(.changeset/20596-service-storage-provenance-anchors.md) is included. It says only that the provenance comments were re-anchored, in stage 5's words.Measured on the built package (A3):
files[]isdist,README.mdandCHANGELOG.md. After the build, the rewritten comments reachdist:f087c376f6 times andda891e0efonce in each ofdist/index.d.tsandindex.d.cts;f087c376f4 times andda891e0efonce in each ofindex.jsandindex.cjs. Positive controls: the unchanged line 「the parent record — the delete rule, applied to the verb that could」 beside the shipped rewrite atattachment-access-hooks.ts:28is found once in each declaration file, and the unchanged line 「standard catalog code — the same both-verbs pairing the derived」 beside the shipped rewrite at:470once in each JS file. A never-written negative phrase appears nowhere indist. None of the 8 dead numbers is left anywhere indist.Gates (head
09d2ecc96)pnpm check:issue-citations(self-test) exits 0.node scripts/check-issue-citations.mjsexits 0: the diff-scoped run judged 3 citations (#12069and#10246resolve;cloud#1395is cross-repo), each already on the line it replaces.pnpm check:doc-authoringexits 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat09d2ecc96derived 65 commands: all 56 derived at dispatch, pluscheck:duration-unit-keys,check:dispatcher-error-vocabulary,check:engine-double-contract,check:logger-receiver-detach,check:objectql-double-limit,check:query-options-erasure,check:type-check-coverage,check:type-check-debtandcheck:where-matcher. Each ran with its exit code captured before any pipe, and all 65 exit 0.--ran, fed each command with its exit code, reports 65 run, 0 NOT MEASURED (a derived zero), 0 unrun, and exits 0. A fullturbo run buildof./packages/*and./packages/*/*ran first under the shared verify lock (71 of 71 tasks, exit 0), so no gate hit an unbuilt workspace.node scripts/check-changeset-fixed.mjs,pnpm check:authz-resolver,pnpm check:error-code-casingandpnpm check:filter-alias-parity, each exit 0.pnpm --filter @objectstack/service-storage test: 40 files pass and 627 tests pass. That is every test file in the package, the 7 touched ones included.pnpm --filter @objectstack/service-storage typecheckexits 0 (tscontsconfig.json, the scripts program, and the test layer ontsconfig.test.json).--listFileson bothtsconfig.jsonandtsconfig.test.jsonshows all 71 files undersrc/, the 40 test files included, and all 15 touched files in the program.eslint --no-inline-config --format jsonover the 15 touched.tsfiles gives 15 files, 0 errors and 0 warnings. All 15 are in eslint's own population (isPathIgnoredis false for each; adistfile, as the control, is ignored).eslint.config.mjsnever enables type-aware linting (noparserOptions.project, as its own lines 327-328 state), so a comment edit here cannot move the verdict on any untouched file. The repo-widepnpm lintis CI's run.pnpm check:nul-bytesexits 0, and a raw scan of the 16 changed files for control bytes finds none.Acceptance notes
CITATION_RErefuses a hyphen after the digits and a/before the#(check-issue-citations closeout (extractor spellings):CITATION_RErefuses a hyphen after the digits, so a dead#N-wordcitation (#13398-class) is invisible to the diff gate and to the census #20636). In this package there is no#N-wordspelling at all. There are 11#A/#Blines carrying 13 second numbers (attachment-access-hooks.ts:215,:217,:434;attachment-access-hooks.test.ts:217;attachment-lifecycle.ts:177;file-reference-lifecycle.test.ts:226;local-storage-adapter.test.ts:35;metadata-store.test.ts:41;storage-route-ledger.ts:74, which chains four;storage-routes.metadata-outage.test.ts:67;tombstone-download-live-reference.test.ts:50), and every second number on them is live:#5574,#9974,#5541,#5480,#3833and#3847by the census's own board, and#5197and#3870read one by one (200). So nothing there needed rewriting. The claim counted 12 such spellings onmain; this reading is 11 lines and 13 second numbers, with nothing dead among them either way. The raw scan above, which sees both spellings, agrees.#13178→f087c376f;#10091→da891e0ef;#11427→c3c72a4bc;#13279→6a180e42d;#6206/#6523→aa4b90d9a;#8778→7901b2dd2;#11671→09b4f4e4e.main(defc7f7b5, read at 19:31Z). None touchesservice-storage,scripts/check-issue-citations.mjsor.changeset/config.json, so there was no merge.Generated by Claude Code