docs(trigger-schedule): re-anchor the dead tracker citations to the commits that decided them - #20775
Conversation
…ommits that decided them Thirty-two comment and docblock sites under packages/triggers/trigger-schedule/src cited tracker numbers that no longer resolve. Each now cites the commit in this repository's history that decided what the line describes: - #16659 -> ecdfc94 (a time-triggered flow declares its acting organization; the run executes as it, the time-relative sweep selects inside it, and a flow declaring none is refused with a throw so the engine records it) - #16589 -> 555a89c (driver-memory refuses a call the engine scoped to a tenant instead of answering every organization's rows) Comments only: 32 lines out, 32 in, every file keeps its line count, and no code token moves. Test titles carrying these numbers are string tokens and are left, as is the one comment that quotes such a title verbatim. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
The rewritten docblocks and inline comments ship: the anchors reach the package's JavaScript entries and its declaration files, so the change publishes bytes and takes a patch changeset. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 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
Coarse fallback — 2 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 c67be4599cb3dc1dc4feba250e4b7dcbae8f7807 && git checkout c67be4599cb3dc1dc4feba250e4b7dcbae8f7807
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c9c182ed14b1ea1a28cbc56fe71a5a373aca0268 14314f49cbb67bf00febd9e3012662a786ec7c41 && git checkout -B drift-repro c9c182ed14b1ea1a28cbc56fe71a5a373aca0268 && git merge --no-ff 14314f49cbb67bf00febd9e3012662a786ec7c41
node scripts/docs-audit/affected-docs.mjs --json c9c182ed14b1ea1a28cbc56fe71a5a373aca0268 |
Contract reviewServed-tier: ① Derived judgmentsRead against
② Semver level
③ Boundary flagsThe dev report (
Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #20596
Clause-②: no
What changed
This is the twelfth stage of the
domain:serviceslane of the dead-citation sweep. It coverspackages/triggers/trigger-schedule/src/**and nothing else. By the seat's claim (5903462246), it is the largest package in the lane that no in-flight work holds, now that #20599's PR #20746 (which editedtime-relative-trigger.ts) has landed. 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 11 (PR #20609 as
422db788a, PR #20626 asb80ab579d, PR #20634 as4d04b6be3, PR #20658 as9a4b2bb38, PR #20693 as0e9ad74fb, PR #20708 as9b384f63a, PR #20717 ascbaf04c1f, PR #20729 asd2820876f, PR #20737 as4dfff176b, PR #20742 as697845d19, PR #20757 ascba417a8f). That is 32 sites on 32 lines in 6 files, covering 2 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: 2 distinct shas. Neither number has an ADR or ruling record of its own (a grep ofdocs/adr/,scripts/adr-anchors/and the rest ofdocs/for both numbers finds nothing, and no ADR records the acting-organization decision or the driver-memory per-call refusal), so both anchors are commits, per ruling C's order. No number was dropped.Only comments changed. Every touched source file keeps its line count (32 lines out, 32 in, over 6 files), so no line citation into these files moves. Every one of the 32 changed lines carried a dead citation; there is no reflow line. No code token moves (see the guard below).
No citation number is added. The only tracker number on an added line is the live
#8844, on the line it already stood on. Added minus removed is negative for the two dead numbers and zero for every other number, and no number is new to the diff. No PR number is the citation on an added line.4 dead sites are left on purpose: three
describetitles, and one comment that quotes one of those titles verbatim (see the list below).One more file: a
patchchangeset for@objectstack/trigger-schedule, because the rewritten prose ships (see Changeset below).Census:
trigger-schedule, 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/triggers/trigger-schedule/. Each run counts as a reading only because its board frontier equals the newest issue or pull-request number, read by a separate request just before and just after the run.allocated-but-absentcba417a8f, run 2026-09-30T03:30:14Z to 03:33:24Z226be8050, run 03:37:59Z to 03:41:09ZThe before count matches the seat's census and A1 (18 sites:
#16659×17 and#16589×1, inschedule-trigger.ts×5 andtime-relative-trigger.ts×13). A1 noted that PR #20746 editedtime-relative-trigger.tstoday; the before count above is taken on the base that already holds that edit. The whole-repo drop is 18, exactly this diff's census sites. Theresolvestally is 33,038 in both runs, andresolves-as-pull-request(1,984) andcross-repo-unjudged(995) did not move either. The after run was taken on226be8050; the head14314f49cadds 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 undertrigger-schedule/src(14 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, 36,840 rows) and did not report it. Every number this package cites is covered by one or the other, so no number needed a separate read to be judged; the two dead numbers were also read one by one on the issues endpoint, and each answers 404.cba417a8f226be8050Its src-comment column equals the census's 18, which is the control on the second instrument. The 123 live citations are the same in both readings, and the drop of 32 citations is exactly the rewritten sites. A third, raw reading (every
#followed by 2 to 6 digits, whatever surrounds it) finds 162 occurrences and 36 dead before, 130 and 4 after. Beyond the gate's grammar it sees 3 tokens, none a tracker reference: the maintainer decision-batch ordinalsbatch #13,#116and#118, which the gate'sNON_CITATION_HEADSexcuses by design.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 (21 lines blame to the anchor itself; for the other 11,merge-base --is-ancestorof anchor and blamed commit exits 0).#16659ecdfc9411(PR #17334): a time-triggered flow (scheduleortime_relative) declares its acting organization on its start node asconfig.organization; the engine lifts it onto the binding; both time triggers refuse to bind a flow that declares none, naming it aterrorand THROWING so the engine records the refusal instead of reporting the flow bound; the run carries the declared organization astenantId; and the time-relative sweep's own query carries it too, so the sweep SELECTS inside that organization (the review finding F2 its diff names), with a store that cannot honour the scope reported aterrorand an object the engine exempts from scoping disclosed at bind. Its body names#16659twice (the three consequences pinned on both drivers, and the proof registered), and its diff names it on 65 added lines. The anchor the spec stage (0f6dcac5e) and the lint stage (f29c83db1) already give the same number#16589555a89cbd(PR #17005):driver-memorygains a third seam,assertCallNotTenantScoped, called first in every driver door that acceptsDriverOptions, which REFUSES a call the engine tenant-scoped instead of discarding the scope and answering every organization's rows; row-level isolation is deliberately not implemented. Its message does not carry the number, but its own diff writes the mechanism the two lines describe and names#1658930 times (the[#16589] Seam 3markers and the guard's docblock), so it is the commit that decided it. New to the sweepEvery cited sha matches exactly one commit (
git rev-parse --disambiguate, count 1 for each of the 2), and both are ancestors of the base (merge-base --is-ancestor, exit 0 for both; control leg: stage 1's landing422db788aexit 0; reverse leg, base againstecdfc9411, exit 1; the history is complete,--is-shallow-repositoryfalse, 15,160 commits; each anchor lies deeper than the control, 1,616 and 1,814 commits behind the base). Each of the 2 numbers answers 404 on the issues endpoint, which serves pull requests too. Independently, the package's own shippedCHANGELOG.mdpairsecdfc94with#16659(line 149) andassertCallNotTenantScopedwith#16589(line 238).Wordings to check
notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659]」 became 「[commit ecdfc94]」 on 18 lines, 「(A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659)」 became 「(commit ecdfc94)」 atschedule-trigger.ts:251,:372andtime-relative-trigger.ts:50, and 「([finding] The InMemory driver silently ignores the engine's tenant scope for objects that OMIT atenancyblock — its guard only refuses an explicitenabled: true, so memory-driver runs show cross-organization rows a SQL driver refuses #16589)」 became 「(commit 555a89c)」 attime-relative-trigger.ts:615andtime-relative-trigger.test.ts:988.schedule-trigger.test.ts:327,time-relative-trigger.test.ts:794and:862: the 16-character phrase replaces the 6-character number and the trailing rule loses 10 characters, so each line keeps its width exactly. The:862heading keeps 「F2」 beside the sha; F2 is the selection findingecdfc9411's own diff names.notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659」 attime-relative-trigger.ts:365and:543became 「before commit ecdfc94」: before that commit the sweep queried withisSystemalone, which is the unscoped selection both sentences describe.notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 defect」 attime-relative-trigger.ts:561andtime-relative-trigger.test.ts:1371became 「the defect commit ecdfc94 fixed」: a commit fixes a defect, it is not one, and the widening both sentences name is the selection half that commit closed.schedule-trigger.test.ts:512. 「the exact defect A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659's own refusal was shaped to avoid」 became 「the exact defect commit ecdfc94's own refusal was shaped to avoid」: the defect is a refusal that logs and arms anyway, and that commit is where the refusal became a throw so the engine records it.schedule-trigger.test.ts:588. 「(the A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 suite above)」 became 「(commit ecdfc94's refusal suite above)」, so the pointer still lands on the refusal suite at:337.The 4 sites left
describetitles, left as stages 1 to 11 left theirs:schedule-trigger.test.ts:337and:462,time-relative-trigger.test.ts:805(all#16659).schedule-trigger.test.ts:71points the reader at 「ScheduleTrigger — the acting-organization refusal (#16659)below」, the exact text of thedescribetitle at:337. The number there belongs to the quotation, so it stays with the title it quotes: rewriting it would point at a title that does not exist. It moves when the title does.src, the package'sCHANGELOG.mdnames#16659on 6 lines and#16589on 2 (lines 149, 153, 238, 273, 297, 300, 302, 304). It is release-owned and deliberately not edited here (see Acceptance notes). The packageREADME.md, which also ships, names neither number.Mechanical guard: no code token moves
The guard compares, base
cba417a8fagainst head, over all 6 touched.tsfiles:forEachChildwalk, so comments are trivia and JSDoc nodes are never visited). String and template literals are therefore read in full.getChildrenwalk, so punctuation and keywords are included; JSDoc nodes skipped).Results:
schedule-trigger.ts(「the same wayscheduleis.」 to 「the same way asschedule.」): 0 files changed, as expected (exit 0).time-relative-trigger.ts(resolveBindingOrganization(binding)givenas FlowTriggerBinding): DIFFER, 1,215 to 1,216 leaf tokens and 2,760 to 2,762 full tokens (exit 1).schedule-trigger.test.ts:462,#16659to#16658): DIFFER on the string literal (exit 1).Every mutation went through
scripts/ablation-replace.mjs(wrap mode) under a shell trap that restores by absolute path, and each landed (anchor 1 to 0, blob changed). Each restore was proven byte-identical to the HEAD blob (651170483856,c85aadbd168d,78a5dea4a463), withgit diff HEADempty and a clean tree afterwards.A first version of reading 2 used TypeScript's context-free scanner and was discarded before any control ran: it opened template tokens on backticks it could not place and swallowed comment text into them, so it reported comment edits as token changes (4 files) while reading 1 read 0. The parser-context stream replaced it, and every figure above is from the replacement.
Changeset
This change ships bytes, so a
patchchangeset for@objectstack/trigger-schedule(.changeset/20596-trigger-schedule-provenance-anchors.md) is included. Its body is stage 11's, word for word, with the package name changed.Measured on the built package (A3):
files[]isdist,README.mdandCHANGELOG.md, and the package is not private. After the build:ecdfc9411appears 3 times in each ofdist/index.jsanddist/index.mjs: the inline comments atschedule-trigger.ts:777andtime-relative-trigger.ts:585and:702, which the bundle keeps.dist/index.d.tsanddist/index.d.mts: theFlowTriggerBinding.organizationdocblock (schedule-trigger.ts:32) and the sweep-context docblock (time-relative-trigger.ts:50).555a89cbdappears once in each JS entry (time-relative-trigger.ts:615).dist.dist.Gates (head
14314f49c)pnpm check:issue-citationsexits 0.node scripts/check-issue-citations.mjsexits 0: the diff-scoped run judged 1 added citation across 2 files, the live#8844, and it resolves.pnpm check:doc-authoringexits 0 (the sibling-package prose-id baseline holds, no growth).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat14314f49c(after a fresh fetch) derived 59 commands. They are all 53 derived at dispatch, pluscheck:engine-double-contract,check:objectql-double-limit,check:query-options-erasure,check:type-check-coverage,check:type-check-debtandcheck:where-matcher.--ran, fed each command with its exit code, reports 59 run, 0 NOT MEASURED (a derived zero), 0 unrun, and exits 0.turbo 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/trigger-schedule test: 8 files pass and 170 tests pass.vitest list --filesOnlynames 8 files, all the tracked test files, the 4 touched ones included.pnpm --filter @objectstack/trigger-schedule typecheckexits 0, andtsc --listFilesholds all 14 files undersrc/, the 6 touched ones included..tsfiles, gives 6 files, 0 errors and 0 warnings (its--format jsonoutput). All 6 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 7 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), andNON_CITATION_HEADSexcuses a number after the word 「option」. In this package:#N-wordnone,#A/#Bnone,option #Nnone, at the base and at the head, which is the claim's 0 / 0 / 0. The twopre-#10220spellings intime-relative-trigger.test.tsare extracted by the gate as this repository's#10220, which the census judges live.CHANGELOG.mdis left.packages/triggers/trigger-schedule/CHANGELOG.mdnames#16659and#16589on 8 lines. It is release-owned (AGENTS.md, Documentation Guardrails), a deferred surface of the citation gate, and ⛔ not part of this stage.schedule-trigger.test.ts:329(「the card's consequence (3)」), sits under the heading:327that now namesecdfc9411, whose own message pins those three consequences, so it keeps a referent. The rest are unchanged, as in stages 8 to 11.#16589→555a89cbdis new to the sweep;driver-memory's ownsrcstill names#16589on 29 lines in 4 files (26 of them comments; corrected by the seat from the dev report, which measured it), all outside this lane's stage surface.#16659→ecdfc9411reuses the spec and lint stages' anchor.mainatcba417a8f.mainhas since moved three commits (0d9349fea,7053333e1,f284ab26d). Their 9 files are one changeset, ADR-0053, and sources and tests underservice-analyticsandservice-automation. They touch nothing undertrigger-schedule, norscripts/check-issue-citations.mjs,.changeset/config.jsonor thedoc-authoring-prose-idbaseline.service-automationis a dev dependency of this package, but this diff moves no code token, so nothing here can interact with it. No merge was taken; the merge queue rebuilds on the merged generation.Generated by Claude Code