docs(plugin-approvals): re-anchor the dead tracker citations to the commits that decided them - #20717
Conversation
…ommits that decided them Stage plugin-approvals of the domain:services dead-citation sweep, in ruling C+D's form C. 41 dead comment and docblock sites on 38 lines in 13 files under packages/plugins/plugin-approvals/src now cite the commit in this repository's history that decided what the line describes, and say in their own words what it decided: the 24 census sites, 15 test-comment sites the census defers, and 2 sites the citation gate's grammar cannot see (a slash-joined second number and an "option #N" citation). 14 numbers, 13 commits; no number has an ADR or ruling record in the repo. Comments only: 41 lines out, 41 in, every file's line count unchanged (1 reflow line, 2 lost-referent lines). No code token changes. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
The rewritten docblocks and inline comments ship in the package's dist (index.d.ts, index.d.mts, index.js, index.mjs), so the released package changes bytes and takes a patch changeset. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 6 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 5b95c208e4243bd03e046aa60468e7f449ec7a25 && git checkout 5b95c208e4243bd03e046aa60468e7f449ec7a25
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 57574637129bebb4a6868c465be7e1b80eed1954 708244c2b2446479422fb533191f9b76cff50231 && git checkout -B drift-repro 57574637129bebb4a6868c465be7e1b80eed1954 && git merge --no-ff 708244c2b2446479422fb533191f9b76cff50231
node scripts/docs-audit/affected-docs.mjs --json 57574637129bebb4a6868c465be7e1b80eed1954
|
Contract reviewServed-tier: ① Derived judgmentsRead against
② Semver level
③ Boundary flagsThe dev report (
Nothing is escalated. Every check on the head was complete and green at this record's final read, so the landing waits on nothing this record can see; the owning seat lands it through the queue as the lane's earlier stages landed. Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #20596
Clause-②: no
What changed
This is the seventh stage of the
domain:serviceslane of the dead-citation sweep. It coverspackages/plugins/plugin-approvals/src/**and nothing else. By the seat's census at the claim (5897866351), 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 6 (PR #20609 as
422db788a, PR #20626 asb80ab579d, PR #20634 as4d04b6be3, PR #20658 as9a4b2bb38, PR #20693 as0e9ad74fb, PR #20708 as9b384f63a). That is 41 sites on 38 lines in 13 files, covering 14 numbers:#8287/#8778(approval-node.test.ts:462) and 「the option Does a DEACTIVATEDsys_positionstill receive sharing-rule shares?expandPositionUsersnever reads the catalogue row, so today it does #8710 rejected」 (approval-service.ts:2329), which the gate reads as an option ordinal.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: 13 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 14 finds none, and a grep of the rest ofdocs/finds only an audit that names#11311as evidence), 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 (41 lines out, 41 in, over 13 files), so no line citation into these files moves. 3 of those 41 lines hold no dead citation: 1 reflow line and 2 lost-referent lines, 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:
#8613(approval-service.ts:2295,:2363,approval-service.test.ts:751),#8287(sys-approval-request.object.ts:138,approval-node.test.ts:462),#10101(backfill-platform-row-organizations.ts:9) and#12069(translations/index.ts:26). Each answers 200. 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; the onePR #Nspelling in scope (backfill-platform-row-organizations.ts:9) became its squash commit.Five dead sites are left on purpose, all of them test strings (see the list below).
One more file: a
patchchangeset for@objectstack/plugin-approvals, because the rewritten docblocks and inline comments ship (see Changeset below).Census:
plugin-approvals, 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/plugins/plugin-approvals/. 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-absent575746371, run 2026-09-29T20:15:05Z to 20:18:28Ze698d2393, run 20:28:39Z to 20:31:58ZThe before count matches the seat's census at the claim and A1 (24 sites). The whole-repo drop is 24, exactly this diff's census sites. The
resolvestally is 32,971 in both runs, andresolves-as-pull-request(1,984) andcross-repo-unjudged(995) did not move either. The after run was taken one698d2393; the head708244c2badds 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 underplugin-approvals/src(76 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) and did not report it. The 18 numbers the census never saw, because they stand only in test files or strings here, were read one by one on the issues endpoint: 14 answer 200, and#8863,#11081,#11286and#11308answer 404.575746371e698d2393Its src-comment column equals the census's 24, which is the control on the second instrument. The 902 live citations and the 32 cross-repo citations are the same in both readings, and the drop of 39 citations is exactly the rewritten sites the gate grammar sees. Three extracted tokens are not citations and stay unjudged in both readings:
'(an HTML entity) and two CSS colours, all inaction-link-pages.tsstring literals. A third, raw reading (every#followed by 2 to 6 digits, whatever surrounds it) finds 46 dead occurrences before and 5 after; the 2 it sees beyond the gate are the two gate-invisible sites above, 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 every rewritten line in its anchor commit or in a later commit that descends from it (merge-base --is-ancestorexit 0 for each pair).#167098c7cca1ce: the three residues of the stranded-inspection contract review. Item 2 (the PM ruling of 2026-09-08) keeps a row whose third read threw in the report as the undifferentiatedfailed; item 3 movesrefineFailedRunStateinside thetry, so a malformed host verdict costs only its own row. Its message numbers the items, which is why the lines keep 「item 2」 and 「item 3」. New to the sweep#871004d03c3a0: a deactivatedsys_positionconfers no sharing-rule shares, filtered at the sharing call site and never inside the addressing primitive. Its message quotes the 2026-08-15 ruling verbatim, the same sentence the quoted blocks here carry, and its diff writes the 「a name with no row is untouched」 fallback thatapproval-service.ts:2318quotes. Stage 2's anchor, andplugin-sharing/src/position-graph.ts:42already reads 「#8613 / commit 04d03c3」#6523aa4b90d9a: the 36 enforcement signatures,IApprovalServiceamong them, converged onto the fullExecutionContext. Its subject names it. Stages 2 and 6 and the spec stage's anchor#6206aa4b90d9a: the same commit, whose body applies 「the #6206 ruling default (converge on the full envelope, keep no per-site subset contracts)」. Written as the full-envelope ruling, the form stages 2 and 6 used#87787901b2dd2: the stamp-onlytenancy.organizationField, Option A of the maintainer's ruling, declared onsys_api_keyasactive_organization_id. The spec,plugin-securityandservice-storagestages' anchor#11081c28e4cfae: the two SqlDriver-backed fixtures of#11081stop muting their kernel and pin the expected read-refusal noise with the runtime's shared capture. Its diff writes all five[#11081]tags. New to the sweep#11286b019891cd: the contract test that pins the twomanagerIsProvablyOutsideOrgscreens to equal verdicts. Its subject names it. New to the sweep#116741cba33f16: the seed loader warns at load time when a seed defers a required column, and the ordering constraint is documented at the four pointer-pair sites, this object among them. Stage 2's anchor for the same paragraph#12493aa5994e17: the Operation Message Catalog gainsapproval_recall_not_submitter(andrecord_write_denied) ahead of their emitters. Its diff names#12493throughout. Stage 2's anchor#87071408fe385: audit rows are stamped from the record's own organization, which its message says the maintainer's ruling on#8287requires; the line keeps 「honouring #8287's ruling」. New to the sweep#8863d200b016b: the two negative pins that assert the unfiltered position expansion on the approvals side. Its body names#8863. New to the sweep#113085a916c4d4: the one-off platform-row organization backfill, dry run and write, which its body calls the#11308sweep. New to the sweep#113111272f0a6b: the squash commit of the pull request that was#11311(its subject carries the number), which moved the resolver tometadata-coreand made the approval and automation-run writers stamp the subject's organization. New to the sweep#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 13), and every one is an ancestor of the base (merge-base --is-ancestor, exit 0 for all 13; the history is complete,--is-shallow-repositoryfalse, 15,129 commits). Each of the 14 numbers answers 404 on the issues endpoint, read one by one;#11311answers 404 on the pulls endpoint too.Wordings to check
approval-node.ts:29,approval-service.ts:52-53andexec-context-annotation.pin.ts:7-8. 「since finding:SharingExecutionContext是同族第四个窄 enforcement 契约类型(sharing / approval / report 三个服务共用),#6206 裁决的「不留 per-site 子集」默认尚未覆盖它 #6523 (the 同族第三处组装:share-link 路由把授权信封裁成 4 个字段后直接当 enforcement context 喂给 engine.find ——group租户姿态下 Layer 0 墙恒判否 #6206 ruling …)」 became 「since commit aa4b90d (the full-envelope ruling …)」, word for word the formplugin-sharing's landedsharing-service.ts:20andexec-context-annotation.pin.ts:7use.approval-service.ts:53is 1 reflow line.approval-service.ts:2295andapproval-service.test.ts:751. 「Maintainer ruling, 2026-08-15 (Does a DEACTIVATEDsys_positionstill receive sharing-rule shares?expandPositionUsersnever reads the catalogue row, so today it does #8710, inheritingsys_permission_set.activeandsys_position.activeare unenforced too — both Deactivate dialogs promise access stops, and it does not #8613), verbatim:」 became 「… (commit 04d03c3, inheritingsys_permission_set.activeandsys_position.activeare unenforced too — both Deactivate dialogs promise access stops, and it does not #8613), verbatim:」. The quotation under it is the ruling itself and is untouched;04d03c3a0's message carries the same sentence.approval-service.ts:2329. 「that is the option Does a DEACTIVATEDsys_positionstill receive sharing-rule shares?expandPositionUsersnever reads the catalogue row, so today it does #8710 rejected」 became 「that is the option the ruling (commit 04d03c3) rejected」.approval-service.test.ts:749. 「the Does a DEACTIVATEDsys_positionstill receive sharing-rule shares?expandPositionUsersnever reads the catalogue row, so today it does #8710 carve-out, asserted on THIS side (plugin-approvalshas no negative pin for its deliberately-unfiltered position expansion — the #8710 carve-out is asserted only on the sharing side #8863)」 became 「the commit 04d03c3 carve-out, asserted on THIS side (commit d200b01)」: the carve-out's record, and the commit that asserted it here.backfill-platform-row-organizations.ts:9. 「PromoteresolveRecordOrganizationFieldto the shared platform-row resolver (approvals + automation runs), per the ruled cloud#1395 Option A #10101 (landed as PR Promote resolveRecordOrganizationField to the shared platform-row resolver: approvals + automation runs stamp the SUBJECT record's organization (cloud#1395 Option A) #11311)」 became 「PromoteresolveRecordOrganizationFieldto the shared platform-row resolver (approvals + automation runs), per the ruled cloud#1395 Option A #10101 (landed as commit 1272f0a)」, the pull request's squash commit.approval-service.ts:4893,:4906andstranded-request-inspection.test.ts:123. 「[[finding] Three residues from the #15358 contract review: an unpinned stale-hot drop, a thrown third read that leavesstranded, and a malformed host verdict that aborts the whole scan #16709 item 3]」 became 「[commit 8c7cca1, item 3]」, and likewise for item 2, beside its 「PM ruling, 2026-09-08」, which8c7cca1ce's message records under 「Item 2」.backfill-platform-row-organizations.test.ts:17「the one thing this card must not do」 became 「the one thing this sweep must not do」, andmanager-org-screen-parity.contract.test.ts:61「the very decision this card is fenced out of」 became 「the very decision this pin is fenced out of」. Each 「this card」 pointed at the number the same comment block opened with, which is now a commit;b019891cd's message says the pin 「PINS the duplication, it does not remove it」.The 5 sites left
describe/ittitles:manager-org-screen-parity.contract.test.ts:232(#11286),stranded-request-inspection.test.ts:519and:642(#16709);manager-org-screen-parity.contract.test.ts:107and:294(#11286).*.source-hashes.generated.tsheaders already cite09b4f4e4eand are untouched. The two verbatim quotations of the 2026-08-15 ruling carry no 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
575746371against head. Template literals are therefore read in context. It ran over all 13 touched.tsfiles.sys-approval-request.object.ts(「who a row is ABOUT」 to 「whom a row is ABOUT」): 0 files changed, as expected (exit 0).sys-approval-request.object.ts(referenceVia: 'object_name',given a trailingas const): DIFFER (exit 1).stranded-request-inspection.test.ts:642): 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 (6cb56301a334,757ad45900ac), withgit diff HEADempty and a clean tree afterwards.Changeset
This change ships bytes, so a
patchchangeset for@objectstack/plugin-approvals(.changeset/20596-plugin-approvals-provenance-anchors.md) is included. Its body is stage 6's, word for word, with the package name changed.Measured on the built package (A3):
files[]isdist,README.mdandCHANGELOG.md. After the build (a cache miss for this package, sodistis this head's source), the rewritten comments reachdist:8c7cca1ce4 times and04d03c3a04 times in each ofdist/index.d.tsandindex.d.mts;04d03c3a04 times,1cba33f16twice, and8c7cca1ce,7901b2dd2and1408fe385once each in each ofindex.jsandindex.mjs. Positive controls: the unchanged line 「A step routing to nobody is」, in the same docblock as the shipped rewrite atapproval-service.ts:2295, is found once in each of the four files, and the unchanged line 「itself stays unwalled (tenancy.enabled: false)」 beside the shipped rewrite atsys-approval-request.object.ts:144once in each JS file. A never-written negative phrase appears nowhere indist. None of the 14 dead numbers is left anywhere indist.Gates (head
708244c2b)pnpm check:issue-citations(self-test) exits 0.node scripts/check-issue-citations.mjsexits 0: the diff-scoped run judged 5 citations across 6 files, and all 5 resolve (#8613twice,#8287,#10101,#12069), each already on the line it replaces.pnpm check:doc-authoringexits 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat708244c2bderived 64 commands: all 57 derived at dispatch, pluscheck:dispatcher-error-vocabulary,check:engine-double-contract,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 64 exit 0.--ran, fed each command with its exit code, reports 64 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/plugin-approvals test: 51 files pass and 791 tests pass. That is every test file in the package, the 7 touched ones included.pnpm --filter @objectstack/plugin-approvals typecheckexits 0 (tscontsconfig.json, the scripts program, and the test layer ontsconfig.test.json, held at its ledger of 8 files, 324 errors and 27 pinned signatures).--listFiles: thetsconfig.jsonprogram holds the 25 non-test files undersrc/, the 6 touched ones included; thetsconfig.test.jsonprogram holds all 76 files undersrc/, the 51 test files and all 13 touched files included.eslint --no-inline-config --format jsonover the 13 touched.tsfiles gives 13 files, 0 errors and 0 warnings. All 13 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 14 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 one#N-wordspelling, 「[P1] approvals: quorum (M-of-N) + grouped per-group sign-off (会签) + decision attachments #3266-era」 (record-reader-visibility.test.ts:342), and two#A/#Bspellings,#8287/#8778(approval-node.test.ts:462) and#8543/#8580(approval-vocabularies.test.ts:66): the claim's 3, 1 and 2.#3266,#8287,#8543and#8580answer 200; the second number#8778is dead, so that one line is rewritten here.NON_CITATION_HEADSexcuses any#Nafter the word 「option」 as an option ordinal, so 「the option Does a DEACTIVATEDsys_positionstill receive sharing-rule shares?expandPositionUsersnever reads the catalogue row, so today it does #8710 rejected」 (approval-service.ts:2329) was never extracted: a dead number there would pass the diff gate at exit 0 and never enter a census count. It is rewritten here. Across the gate's declared surfaces at the base, the only otheroption #Nwith three or more digits ispackages/objectql/src/validation/rule-validator.ts:2202(option #14088), which answers 200. Same family as 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; noted for its closeout, not a card of its own.tenancy.organizationFieldleft the authorable surface in502f179cc, and limb 0 of the shared resolver now readsPLATFORM_STAMP_ORGANIZATION_COLUMNSinmetadata-core, keyed by object name. Comments in this package still name the retired key as what limb 0 reads (sys-approval-request.object.ts:143, the line above a rewrite;backfill-platform-row-organizations.ts:35,approval-node.test.ts:463,approval-service.ts:2707,backfill-platform-row-organizations.test.ts:50), and two test fixtures still declare it on a stubsys_api_key(approval-node.test.ts:467,backfill-platform-row-organizations.test.ts:54), where it is inert because the resolver keys by name. The anchor7901b2dd2is right for the key those lines name, and nothing is wrong at runtime. Correcting the prose would reach past the dead citations, and the fixtures are code tokens, so none of it is changed here.#16709→8c7cca1ce;#11081→c28e4cfae;#11286→b019891cd;#11308→5a916c4d4;#11311→1272f0a6b;#8707→1408fe385;#8863→d200b016b.mainat575746371, which is stillmainat 20:56Z (read into a private ref), so there was no merge.Generated by Claude Code