Skip to content

docs(trigger-schedule): re-anchor the dead tracker citations to the commits that decided them - #20775

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-trigger-schedule-citations
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-trigger-schedule-citations

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Part of #20596
Clause-②: no

What changed

This is the twelfth stage of the domain:services lane of the dead-citation sweep. It covers packages/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 edited time-relative-trigger.ts) has landed. Later stages cover the other packages, so this PR says Part of and 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 as b80ab579d, PR #20634 as 4d04b6be3, PR #20658 as 9a4b2bb38, PR #20693 as 0e9ad74fb, PR #20708 as 9b384f63a, PR #20717 as cbaf04c1f, PR #20729 as d2820876f, PR #20737 as 4dfff176b, PR #20742 as 697845d19, PR #20757 as cba417a8f). That is 32 sites on 32 lines in 6 files, covering 2 numbers:

  • 18 census sites (every census site this package has);
  • 14 sites in test comments, which the census defers;
  • no site the gate's grammar cannot see (the package has none, see Acceptance notes).

Each rewritten line now cites the commit in origin/main history 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 of docs/adr/, scripts/adr-anchors/ and the rest of docs/ 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 describe titles, and one comment that quotes one of those titles verbatim (see the list below).

One more file: a patch changeset for @objectstack/trigger-schedule, because the rewritten prose ships (see Changeset below).

Census: trigger-schedule, before and after

Instrument (A1). The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count below is its allocated-but-absent findings under packages/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.

reading tree board whole-repo allocated-but-absent trigger-schedule sites lines files numbers
before base cba417a8f, run 2026-09-30T03:30:14Z to 03:33:24Z enumerated, 186 pages, frontier #20769 (newest #20769 before and after) 823 18 18 2 2
after head 226be8050, run 03:37:59Z to 03:41:09Z enumerated, 186 pages, frontier #20769 (newest #20769 before and after) 805 0 0 0 0

The before count matches the seat's census and A1 (18 sites: #16659 ×17 and #16589 ×1, in schedule-trigger.ts ×5 and time-relative-trigger.ts ×13). A1 noted that PR #20746 edited time-relative-trigger.ts today; 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. The resolves tally is 33,038 in both runs, and resolves-as-pull-request (1,984) and cross-repo-unjudged (995) did not move either. The after run was taken on 226be8050; the head 14314f49c adds 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) and namesThisRepository over every .ts file under trigger-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 it allocated-but-absent, and alive when that census judged it on this board anywhere (its --list extraction, 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.

reading citations dead src comment test comment src string test string
before, cba417a8f 159 36 18 15 0 3
after, 226be8050 127 4 0 1 0 3

Its 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 ordinals batch #13, #116 and #118, which the gate's NON_CITATION_HEADS excuses by design.

Per-number table

Sites and files count every dead occurrence in scope at the base (comments and strings, tests included). rewritten / left counts the sites rewritten and the sites left. Each anchor was read in its message and diff, not only its subject, and git blame at 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-ancestor of anchor and blamed commit exits 0).

number sites / files rewritten / left anchor: what it decided
#16659 34/6 30/4 ecdfc9411 (PR #17334): a time-triggered flow (schedule or time_relative) declares its acting organization on its start node as config.organization; the engine lifts it onto the binding; both time triggers refuse to bind a flow that declares none, naming it at error and THROWING so the engine records the refusal instead of reporting the flow bound; the run carries the declared organization as tenantId; 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 at error and an object the engine exempts from scoping disclosed at bind. Its body names #16659 twice (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
#16589 2/2 2/0 555a89cbd (PR #17005): driver-memory gains a third seam, assertCallNotTenantScoped, called first in every driver door that accepts DriverOptions, 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 #16589 30 times (the [#16589] Seam 3 markers and the guard's docblock), so it is the commit that decided it. New to the sweep

Every 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 landing 422db788a exit 0; reverse leg, base against ecdfc9411, exit 1; the history is complete, --is-shallow-repository false, 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 shipped CHANGELOG.md pairs ecdfc94 with #16659 (line 149) and assertCallNotTenantScoped with #16589 (line 238).

Wordings to check

The 4 sites left

  • Test strings, 3 sites on 3 lines, all describe titles, left as stages 1 to 11 left theirs: schedule-trigger.test.ts:337 and :462, time-relative-trigger.test.ts:805 (all #16659).
  • One comment that quotes a kept title verbatim: schedule-trigger.test.ts:71 points the reader at 「ScheduleTrigger — the acting-organization refusal (#16659) below」, the exact text of the describe title 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.
  • No source string, operator log string, assertion message, quoted maintainer ruling or generated file in this package carries a dead number.
  • Outside src, the package's CHANGELOG.md names #16659 on 6 lines and #16589 on 2 (lines 149, 153, 238, 273, 297, 300, 302, 304). It is release-owned and deliberately not edited here (see Acceptance notes). The package README.md, which also ships, names neither number.

Mechanical guard: no code token moves

The guard compares, base cba417a8f against head, over all 6 touched .ts files:

  • Reading 1, the TypeScript parser's leaf nodes (a forEachChild walk, so comments are trivia and JSDoc nodes are never visited). String and template literals are therefore read in full.
  • Reading 2, the full token stream in parser context (a getChildren walk, so punctuation and keywords are included; JSDoc nodes skipped).

Results:

  • Real run: 10,192 base leaf tokens, 0 files with a token change on either reading (exit 0).
  • Comment control in schedule-trigger.ts (「the same way schedule is.」 to 「the same way as schedule.」): 0 files changed, as expected (exit 0).
  • Positive control, a code token added in time-relative-trigger.ts (resolveBindingOrganization(binding) given as FlowTriggerBinding): DIFFER, 1,215 to 1,216 leaf tokens and 2,760 to 2,762 full tokens (exit 1).
  • Positive control, one digit changed inside a kept test title (schedule-trigger.test.ts:462, #16659 to #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), with git diff HEAD empty 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 patch changeset 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[] is dist, README.md and CHANGELOG.md, and the package is not private. After the build:

  • ecdfc9411 appears 3 times in each of dist/index.js and dist/index.mjs: the inline comments at schedule-trigger.ts:777 and time-relative-trigger.ts:585 and :702, which the bundle keeps.
  • It appears twice in each of dist/index.d.ts and dist/index.d.mts: the FlowTriggerBinding.organization docblock (schedule-trigger.ts:32) and the sweep-context docblock (time-relative-trigger.ts:50).
  • 555a89cbd appears once in each JS entry (time-relative-trigger.ts:615).
  • Positive controls, one unchanged line beside each shipped rewrite, land exactly where their neighbours do: four neighbours once in each JS file and 0 in the declaration files, and two once in each declaration file and 0 in the JS files.
  • A never-written negative phrase appears nowhere in dist.
  • Neither dead number is left in dist.

Gates (head 14314f49c)

  • Citation judging, as CI runs it: pnpm check:issue-citations exits 0. node scripts/check-issue-citations.mjs exits 0: the diff-scoped run judged 1 added citation across 2 files, the live #8844, and it resolves.
  • Doc authoring: pnpm check:doc-authoring exits 0 (the sibling-package prose-id baseline holds, no growth).
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 14314f49c (after a fresh fetch) derived 59 commands. They are all 53 derived at dispatch, plus check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt and check:where-matcher.
    • Each ran with its exit code captured before any pipe, and all 59 exit 0.
    • --ran, fed each command with its exit code, reports 59 run, 0 NOT MEASURED (a derived zero), 0 unrun, and exits 0.
    • A full turbo run build of ./packages/* and ./packages/*/* ran first under the shared verify lock (71 of 71 tasks, exit 0), so no gate hit an unbuilt workspace.
  • Roster families the derivation lists outside its commands (their rosters sit in directories this diff touches): node scripts/check-changeset-fixed.mjs, pnpm check:authz-resolver, pnpm check:error-code-casing and pnpm check:filter-alias-parity, each exit 0.
  • Tests and typecheck, under the verify lock:
    • pnpm --filter @objectstack/trigger-schedule test: 8 files pass and 170 tests pass. vitest list --filesOnly names 8 files, all the tracked test files, the 4 touched ones included.
    • pnpm --filter @objectstack/trigger-schedule typecheck exits 0, and tsc --listFiles holds all 14 files under src/, the 6 touched ones included.
  • Lint, as a proven narrowing: eslint with inline config disabled, over the 6 touched .ts files, gives 6 files, 0 errors and 0 warnings (its --format json output). All 6 are in eslint's own population (isPathIgnored is false for each; a dist file, as the control, is ignored). eslint.config.mjs never enables type-aware linting (no parserOptions.project, as its own lines 327-328 state), so a comment edit here cannot move the verdict on any untouched file. The repo-wide pnpm lint is CI's run.
  • Control bytes: pnpm check:nul-bytes exits 0, and a raw scan of the 7 changed files for control bytes finds none.

Acceptance notes

  • The gate-invisible spellings, grepped as the claim asked. CITATION_RE refuses a hyphen after the digits and a / before the # (check-issue-citations closeout (extractor spellings): CITATION_RE refuses a hyphen after the digits, so a dead #N-word citation (#13398-class) is invisible to the diff gate and to the census #20636), and NON_CITATION_HEADS excuses a number after the word 「option」. In this package: #N-word none, #A/#B none, option #N none, at the base and at the head, which is the claim's 0 / 0 / 0. The two pre-#10220 spellings in time-relative-trigger.test.ts are extracted by the gate as this repository's #10220, which the census judges live.
  • CHANGELOG.md is left. packages/triggers/trigger-schedule/CHANGELOG.md names #16659 and #16589 on 8 lines. It is release-owned (AGENTS.md, Documentation Guardrails), a deferred surface of the citation gate, and ⛔ not part of this stage.
  • 「The card」 phrases are left. 8 comment lines in 5 files of this package speak of 「this card」, 「that card」 or 「the card」. They carry no number and neither instrument sees them. The one beside a rewritten line, schedule-trigger.test.ts:329 (「the card's consequence (3)」), sits under the heading :327 that now names ecdfc9411, whose own message pins those three consequences, so it keeps a referent. The rest are unchanged, as in stages 8 to 11.
  • The census instrument did not truncate in this stage. Both enumerations read 186 pages at the newest frontier.
  • Anchors the next stages can reuse, each checked here: #16589 → 555a89cbd is new to the sweep; driver-memory's own src still names #16589 on 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 → ecdfc9411 reuses the spec and lint stages' anchor.
  • Base. The branch is on main at cba417a8f. main has since moved three commits (0d9349fea, 7053333e1, f284ab26d). Their 9 files are one changeset, ADR-0053, and sources and tests under service-analytics and service-automation. They touch nothing under trigger-schedule, nor scripts/check-issue-citations.mjs, .changeset/config.json or the doc-authoring-prose-id baseline. service-automation is 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

…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>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Sep 30, 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

Coarse fallback — 2 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 c9c182ed14b1ea1a28cbc56fe71a5a373aca0268 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c67be4599cb3dc1dc4feba250e4b7dcbae8f7807 — the merge of head 14314f49cbb67bf00febd9e3012662a786ec7c41 into base c9c182ed14b1ea1a28cbc56fe71a5a373aca0268, 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 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

⚠️ 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: 14314f49cbb67bf00febd9e3012662a786ec7c41
Local-runs: none

① Derived judgments

Read against main at the merge-base cba417a8f (stage 11's landing), from the branch fetched into an owned ref in the shared object store; tree, card and check-runs read 2026-09-30T04:31Z. The PR's recorded base c9c182ed1 is 4 commits on from the merge-base; those 4 commits touch 18 files, none under trigger-schedule, scripts/check-issue-citations.mjs, .changeset/config.json or the prose-id baseline, so the net diff against main is the merge-base diff: 7 files, +42/−32, 2 commits.

  • Accept-set: no change — right. Every one of the 32 removed and 32 added source lines opens with a comment marker after whitespace; 0 changed source lines fall outside one (counted over the diff, independently of the dev's parser guard). No Zod schema, REST handler, query-parameter set, refusal text, log string or runtime string moves. Each of the 6 touched files keeps its line count at the head (504, 141, 734, 1015, 1537, 880), so no line citation into these files moves.
  • Public surface: no change — right. No export added, removed or renamed; no packages/spec file touched, so no generated artifact is owed. Rewritten docblocks sit on exported declarations (FlowTriggerBinding.organization, resolveBindingOrganization, the TimeRelativeDataEngine sweep context), which changes declaration-file bytes, not the surface.
  • Published bytes: changed — right, and it decides ②. @objectstack/trigger-schedule 17.5.0 is not private, files = dist, README.md, CHANGELOG.md, and the rewritten inline comments and docblocks sit in shipped source. The dev's A3 build reading with positive and negative controls says the same; this record does not repeat the build.
  • The 2 re-anchors — each right, read independently against the object store and the board:
    • #16659 → ecdfc9411: exactly one commit (rev-parse --disambiguate count 1), an ancestor of the base, the squash of PR fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334. Its message says a time-triggered flow declares its acting organization on its start node, both time triggers refuse to bind a flow that declares none and THROW so the engine records the refusal, and the run carries the declaration as tenantId; its diff removes the sweep's isSystem-only query context and adds tenantId beside it, adds the throw in schedule-trigger.ts, and names F2 on 5 added lines. So the SELECTION-half heading at time-relative-trigger.test.ts:862, the two "before commit ecdfc94" wordings (time-relative-trigger.ts:365, :543) and the "defect commit ecdfc94 fixed" wordings all state what that commit did. Its message names #16659 twice; #16659 answers 404 on the issues endpoint.
    • #16589 → 555a89cbd: exactly one commit, an ancestor of the base, the squash of PR fix(driver-memory): refuse a tenant-scoped call instead of returning cross-organization rows #17005. Its message says driver-memory gains assertCallNotTenantScoped, called first in every door that accepts DriverOptions, refusing a call the engine tenant-scoped instead of answering every organization's rows — the sentence both rewritten lines (time-relative-trigger.ts:615, time-relative-trigger.test.ts:988) now carry. Its message does not name the number; its diff names #16589 on 28 added lines. #16589 answers 404.
    • The anchor's self-citation counts differ slightly between readings (this record: 62 and 28 added lines; the PR body: 65 and 30, occurrences rather than lines) — not load-bearing to either anchoring.
    • git blame at the base over all 32 rewritten lines: 21 blame to ecdfc9411 itself, 11 to commits that descend from it (merge-base --is-ancestor exit 0 for each), 0 to anything else — every anchor precedes the line it now stands on.
  • Citation accounting — right. Removed source lines carry #16659 ×30, #16589 ×2, #8844 ×1; added source lines carry #8844 ×1 (the same line, time-relative-trigger.ts:365), commit ecdfc9411 ×30 and commit 555a89cbd ×2. No number is new to an added line, added-minus-removed is negative for both dead numbers and zero for #8844, and no PR #N spelling stands on an added line. #8844 resolves (closed, completed).
  • The 4 sites left — right, and the list is exact. A grep of both numbers over trigger-schedule/src at the head returns exactly 4 lines: the three describe titles (schedule-trigger.test.ts:337, :462, time-relative-trigger.test.ts:805) and the comment at schedule-trigger.test.ts:71 that quotes the :337 title verbatim. Strings are the form-D lane, left as stages 1 to 11 left theirs; a number inside a quotation belongs to the quotation and moves with the title. The package CHANGELOG.md names both numbers on 8 lines and is release-owned; rightly untouched.
  • Wordings — right. The three section rules give up 10 rule glyphs for the 10 characters the phrase gains, so each keeps its character width (the byte length drops by 20 because the rule glyph is three bytes; that is not a width change). No reflow line: every changed line carried a dead number.
  • Form — consistent with stages 1 to 11: the word commit plus the abbreviated sha in the position the number held, the decision restated in the sentence. The changeset is stage 11's body with the package name swapped (a diff of the two files shows two lines, both the name). Both commits' trailers are the repo's model-free pair.
  • Check-runs on the head, the gate verdicts (read 2026-09-30T04:31Z): 41 check-runs, all completed — 0 failure, 0 in progress. Latest run per name: every gate-carrying check success; skipped only on Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in) (paths-filtered or opt-in) and on the second Auto Label and Check PR Size runs re-triggered by the seat's body edit (the first run of each is success). All seven required contexts are success: Lint & Repo Gates (which carries check:issue-citations and check:doc-authoring, the two gates this diff answers to), TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. Nothing was built, run or re-run locally; the git reads (show, diff, blame, merge-base, grep) were against the shared object store.

② Semver level

  • .changeset/20596-trigger-schedule-provenance-anchors.md declares '@objectstack/trigger-schedule': patch — matches what the diff publishes. The package is released and its dist carries the rewritten comments and docblocks, so bytes ship; skip-changeset would be wrong (it is for a diff that publishes nothing from any released package). Not minor: no accept set widens and no surface is added. The body is truthful (comments only; no type, schema, export, log or refusal text, or runtime behaviour change). The package sits in the fixed group, so the patch lifts the group exactly as every earlier stage's did; Check Changeset on the head is success.
  • Clause-②: no — right. It is line 2 of the PR body, and the claim (5903462246) declares the same. The diff widens no accept set, so no arm is owed and no minor is owed. Nothing breaks, so no ADR-0087 marker is owed.
  • Not a governed-surface diff (no docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md); 74 changed lines, under the 5,000-line human-merge threshold; Governed Surface Queue Guard on the head is success. The PR is a draft with Part of #20596 on line 1 and no closing keyword, so the card stays open for the next stage.

③ Boundary flags

The dev report (5903952809) has open_questions: []. Its 13 deviations and 3 out-of-scope findings, each answered:

  1. 14 test-comment sites beyond the census's 18 — answered, in scope. The claim's surface is comment and docblock prose under trigger-schedule/src/**; test comments are that. The head grep above confirms the residue.
  2. schedule-trigger.test.ts:71 kept as a verbatim quotation of a kept describe title — answered, right. Rewriting it would point at a title that does not exist; it moves when the title does.
  3. Wordings beyond the tag swap — answered, right. Each sits on a line that carried a dead number, each states what the anchor commit did, and the section-rule widths hold (checked above).
  4. Instrument correction on the token guard's second reading — answered, immaterial to this record. The guard's figures are the dev's; this record's own comment-marker count over the diff (0 changed source lines outside a comment) reaches the same conclusion by a different reading.
  5. Read-only MCP reads, no curl carrying a token — answered, nothing to change.
  6. Supplementary and raw instruments judged against the before census's board reading — answered, stage 6's method; the census's own 18 → 0 is the control.
  7. A gate batch outlasting the call cap, waited on by its recorded PID — answered, nothing lost; the head's check-runs are the verdicts this record reads in any case.
  8. The harness attribution reminder declined in favour of the model-free trailer pair — answered, right. Both commits on the fetched ref carry that pair and no model identifier.
  9. No pre-PR merge of main — answered, right. The 4 commits since the base touch nothing in scope (read above); the merge queue rebuilds on the merged generation.
  10. Labels — answered. documentation, size/s, tests, tooling are the labeler's; nothing to change.
  11. Body read-back byte comparison NOT MEASURED — answered. The seat's ACCEPT (5903988920) read the body on GitHub and patched one count; this record read the body through the API and found every section present with one footer.
  12. Cleanup — answered, nothing to change.
  13. The wrong driver-memory count in the PR body — answered by the seat, verified here. The body now reads "29 lines in 4 files (26 of them comments)", as the ACCEPT says it was patched.
  14. Out-of-scope 1, URL-spelled citations invisible to both grammars (96 under packages/**/src, 12 dead), and driver-memory's refusal string building a URL naming dead #16589 — escalated to its carriers, not to this PR. The ACCEPT folds the URL spelling into check-issue-citations closeout (extractor spellings): CITATION_RE refuses a hyphen after the digits, so a dead #N-word citation (#13398-class) is invisible to the diff gate and to the census #20636 (the extractor-spelling family) and sends the runtime string to [finding] runtime warnings outside the migration ledger print tracker numbers to authors and operators: the AutomationEngine resumeAuthority boot warning (#3801 / #5561 / #3823) and two objectql data-event warnings (#4639 / #4626) #20513 (form D), each with a pointer. Documentation rot and a form-D string are not this stage's defect; nothing for the dev.
  15. Out-of-scope 2, "this card" on 8 comment lines — answered. Wording only, no number, noted in the PR's Acceptance notes; the one beside a rewritten heading keeps a referent through ecdfc9411's own message.
  16. Out-of-scope 3, driver-memory's 26 dead #16589 comment lines — answered, carrier stands. The engine lane's citation stage (dead tracker citations in the domain:engine packages (645 sites, 160 numbers, 104 files): the ruling C+D stage for this lane (from #20556) #20595) per the ACCEPT, with 555a89cbd measured here as a reusable anchor. Not this lane's file surface.

Implemented-by: claude/issue-20596-trigger-schedule-citations
Reviewed-by: session_01XY5uCwTjZj7884yYtyur4H

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 04:32
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 91e8fa1 Sep 30, 2026
43 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20596-trigger-schedule-citations branch September 30, 2026 04:51
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…the commits that decided them (objectstack-ai#20789)

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

## What changed

This is the thirteenth stage of the `domain:services` lane of the
dead-citation sweep. It covers
`packages/triggers/trigger-record-change/src/**` and nothing else. By
the seat's claim (`5904332626`), it is the largest package in the lane
that no in-flight work holds, while `service-automation` stays held
behind objectstack-ai#20726. Later stages cover the other packages, so this PR says
`Part of` and 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 objectstack-ai#19123), by the method of stages 1 to 12 (PR objectstack-ai#20609 as
`422db788a`, PR objectstack-ai#20626 as `b80ab579d`, PR objectstack-ai#20634 as `4d04b6be3`, PR
objectstack-ai#20658 as `9a4b2bb38`, PR objectstack-ai#20693 as `0e9ad74fb`, PR objectstack-ai#20708 as
`9b384f63a`, PR objectstack-ai#20717 as `cbaf04c1f`, PR objectstack-ai#20729 as `d2820876f`, PR
objectstack-ai#20737 as `4dfff176b`, PR objectstack-ai#20742 as `697845d19`, PR objectstack-ai#20757 as
`cba417a8f`, PR objectstack-ai#20775 as `91e8fa194`). That is **29 sites on 29 lines
in 5 files, covering 3 numbers**:

- 6 census sites (every census site this package has, all `objectstack-ai#14744`);
- 23 sites in test comments, which the census defers: 17 more of
`objectstack-ai#14744`, 1 of `objectstack-ai#13657`, and 5 of `objectstack-ai#11081`. `objectstack-ai#11081` stands only in a
test file here, so the census never judged it; it was read on its own
and answers 404.

Each rewritten line now cites the commit in `origin/main` history that
decided what the line describes, and says in its own words what was
decided: **4 distinct shas**. None of the three numbers has an ADR or
ruling record of its own, so every anchor is a commit, per ruling C's
order (see the per-number table). No number was dropped.

Only comments changed. Every touched source file keeps its line count
(30 lines out, 30 in, over 5 files), so no line citation into these
files moves. 29 of the 30 changed lines carried a dead citation; the
thirtieth keeps a referent the rewrite would otherwise have removed (see
Wordings). No code token moves (see the guard below).

**No citation number is added.** The only tracker numbers on added lines
are the live `objectstack-ai#15356` (3 times) and `objectstack-ai#8738` (once), each on the line it
already stood on. Added minus removed is negative for the three 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, all test titles (see the list below).

One more file: a `patch` changeset for
`@objectstack/trigger-record-change`, because the rewritten prose ships
(see Changeset below).

## Census: `trigger-record-change`, before and after

**Instrument (A1).** The gate's own `node
scripts/check-issue-citations.mjs --census --json`, read-only and
unchanged. The count below is its `allocated-but-absent` findings under
`packages/triggers/trigger-record-change/`. 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.
In all three runs a new number was opened while the run was enumerating;
each frontier equals the newest number at the run's end, which is the
criterion (stages 7 and 11 met the same shape).

| reading | tree | board | whole-repo `allocated-but-absent` |
trigger-record-change sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `91e8fa194`, run 2026-09-30T04:58:08Z to 05:01:28Z |
enumerated, 187 pages, frontier objectstack-ai#20779 (newest objectstack-ai#20778 before, objectstack-ai#20779
after) | 802 | **6** | 6 | 2 | 1 |
| after | `bb9d39a87` (the comments commit), run 05:07:54Z to 05:11:48Z
| enumerated, 187 pages, frontier objectstack-ai#20780 (newest objectstack-ai#20779 before, objectstack-ai#20780
after) | 796 | **0** | 0 | 0 | 0 |
| after, final head | head `bbfe7cb24`, run 05:39:10Z to 05:42:26Z |
enumerated, 187 pages, frontier objectstack-ai#20784 (newest objectstack-ai#20783 before, objectstack-ai#20784
after) | 796 | **0** | 0 | 0 | 0 |

The before count matches the seat's census and A1 (6 sites, all
`objectstack-ai#14744`: `decouple-flow-record.ts` ×1 and `record-change-trigger.ts`
×5). The whole-repo drop is 6, exactly this diff's census sites. The
`resolves` tally is 33,055 in all three runs, and
`resolves-as-pull-request` (1,984) and `cross-repo-unjudged` (995) did
not move either. No run was truncated or discarded: all three
enumerations read 187 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) and `namesThisRepository`
over every `.ts` file under `trigger-record-change/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 it `allocated-but-absent`, and alive when the gate's own
census-scope extraction (36,836 citations over 2,617 files) judged it
and the census did not report it. Five numbers are covered by neither,
because they stand only in test files: each was read on its own.
`objectstack-ai#11081` answers 404; `objectstack-ai#5715` and `objectstack-ai#17982` answer 200 as pull requests;
`objectstack-ai#5785` and `objectstack-ai#17985` answer 200 as issues. The three dead numbers were
also read one by one, and each answers 404.

| reading | citations | dead | src comment | test comment | src string |
test string |
|---|---|---|---|---|---|---|
| before, `91e8fa194` | 186 | **32** | 6 | 23 | 0 | 3 |
| after, `bbfe7cb24` | 157 | **3** | 0 | 0 | 0 | 3 |

Its src-comment column equals the census's 6, which is the control on
the second instrument. The 154 live citations are the same in both
readings, and the drop of 29 citations is exactly the rewritten sites. A
third, raw reading (every `#` followed by 2 to 6 digits, whatever
surrounds it) finds 195 occurrences before and 166 after. Beyond the
gate's grammar it sees 9 tokens, the same at base and head: the second
number of five `#A/#B` pairs (only one is dead, the kept title at
`before-update-flow-payload-reach.test.ts:872`), two `/objectstack-ai#3457/` regex
literals in assertions (live), and two `PD objectstack-ai#12` ordinals.

## Per-number table

Sites and files count every dead occurrence in scope at the base
(comments and strings, tests included). `rewritten / left` counts the
sites rewritten and the sites left. Each anchor was read in its message
and diff, not only its subject.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#14744` | 27/4 | 22/4 | `4f85e4d11` (PR objectstack-ai#15475): the flow-facing
`record` (and its `params` alias) and `previous` are decoupled from the
engine's own objects before a flow runs (`decoupleFromEngineState`:
arrays, plain objects, `Date`, `RegExp`, `Map` and `Set` are copied,
primitives, functions and other class instances shared), so a flow
mutating a nested value in place no longer writes the batch payload that
ADR-0058 Addendum II D3 shares across every row of a `multi: true`
update. A COPY rather than a FREEZE, because `expandDeclaredLookups`
writes into the record it is handed. The engine's write shape is
unchanged, and the same-key per-row-value residue is deliberately left
unguarded. Its changeset records the maintainer's option-A ruling on
`objectstack-ai#14744` in its own words, its diff names `objectstack-ai#14744` on 29 added lines,
and it created `decouple-flow-record.ts` and both of this package's pin
files. `git blame` at the base puts every one of the 22 lines in this
commit. New to the sweep |
| `objectstack-ai#14744` (the census line) | (in the row above) | 1/0 | `03c1b0f6f`
(PR objectstack-ai#15301): the census of same-key / per-row-VALUE `beforeUpdate`
rewrites, which found ZERO across 23 production registration sites and
recorded the `buildContext` overlay conclusion as a source reading, not
a measurement. Its message names `objectstack-ai#14744` four times and states that
result word for word. `before-update-flow-payload-reach.test.ts:29`
describes this census, not the fix, so it cites the census commit, by
the per-arm precedent of stages 5 and 9. The line was written by
`4f85e4d11`, which descends from `03c1b0f6f` (`merge-base --is-ancestor`
exit 0). New to the sweep |
| `objectstack-ai#13657` | 1/1 | 1/0 | `b003cf2e8` (PR objectstack-ai#13864): the post-hook half of
the declared-field door, which refuses an undeclared field a before-hook
writes, with one envelope on every driver. Its message names `objectstack-ai#13657`
seven times. The runtime and lint stages' anchor for the same number.
The line was written by `4f85e4d11`, which descends from it (exit 0) |
| `objectstack-ai#11081` | 5/1 | 5/0 | `c28e4cfae` (PR objectstack-ai#11570): the two
SqlDriver-backed fixtures stop blanket-silencing their kernel and carry
`@objectstack/runtime`'s shared expected-noise capture, which withholds
only a declared table's own `no such table` line, forwards every other
driver fault, and lets `afterAll` assert each channel fired. Its message
names `objectstack-ai#11081`, and its diff writes the five `[objectstack-ai#11081]` tags in this
very file; `git blame` at the base puts all five lines in it. Stage 7's
anchor for the same number |

Every cited sha matches exactly one commit (`git rev-parse
--disambiguate`, count 1 for each of the 4), and all 4 are ancestors of
the base (`merge-base --is-ancestor`, exit 0 for each; reverse leg, base
against each anchor, exit 1 for each; control legs exit 0: stage 1's
landing `422db788a`, and the repository's root commit, which lies deeper
than every anchor; the history is complete, `--is-shallow-repository`
false, 15,167 commits; the anchors lie 2,516, 2,585, 3,082 and 4,207
commits behind the base). Each of the 3 numbers answers 404 on the
issues endpoint, which serves pull requests too.

No ADR, `scripts/adr-anchors/` file or other `docs/` page records any of
the three as its decision.
`docs/audits/2026-09-multi-update-per-row-value-census.md` names
`objectstack-ai#14744`, but it states that it is "measurement only — ships nothing …
implements no guard", the input to a decision rather than its record, so
the census line cites the commit that landed it.

## Wordings to check

- **Tag swaps in brackets or parentheses.** 「[objectstack-ai#14744]」 became 「[commit
4f85e4d]」 at `decouple-flow-record.test.ts:4` and
`before-update-flow-payload-reach.test.ts:805`. 「[objectstack-ai#11081]」 became
「[commit c28e4cf]」 on 5 lines. 「(objectstack-ai#14744, measured by objectstack-ai#15356)」 became
「(commit 4f85e4d, measured by objectstack-ai#15356)」 at `decouple-flow-record.ts:5`.
「(objectstack-ai#14744)」 became 「(commit 4f85e4d)」 at
`record-change-trigger.ts:340`. 「(objectstack-ai#8738 pre-hook / objectstack-ai#13657 post-hook)」
became 「(objectstack-ai#8738 pre-hook / commit b003cf2 post-hook)」.
- **Headings `:4` and `:859`.** 「[objectstack-ai#15356 measured, objectstack-ai#14744 closed]」 and
「[objectstack-ai#15356 measured it, objectstack-ai#14744 closed it]」 keep the live `objectstack-ai#15356` and put
the sha where the dead number stood.
- **`before-update-flow-payload-reach.test.ts:10`.** 「objectstack-ai#14744 then ruled
the door closed」 became 「The option-A ruling (commit 4f85e4d) then
closed the door」: the ruling is named in words beside the commit that
carried it, whose changeset records it, the form stages 2, 6 and 7 used
for a ruling.
- **`:22` and `:87`.** 「the objectstack-ai#14744 residue shape」 and 「the objectstack-ai#14744 pinned
residue shape」 became 「the residue shape commit 4f85e4d pins」: the
positive control that pins it is in that commit's diff.
- **`:23`.** 「because objectstack-ai#14744's fix is about aliasing」 became 「because
commit 4f85e4d fixes aliasing」: a commit fixes something, it does not
have a fix.
- **`:29` and `:34`, the census paragraph.** 「objectstack-ai#14744's census found」
became 「The census in commit 03c1b0f found」. That removed the referent
of 「The conclusion recorded on that card」 five lines down, so `:34`
became 「The conclusion recorded in that census」. This is the one changed
line that carried no dead number. It is true as written: the census
record `03c1b0f6f` landed carries that very conclusion, "On a source
reading, `buildContext` materialises a *new* record object by overlay …
a reading, not a measurement"
(`docs/audits/2026-09-multi-update-per-row-value-census.md:308-311`).
- **`:455`.** 「that is precisely the blind spot objectstack-ai#14744 is weighing」
became 「… the blind spot commit 4f85e4d left unguarded」. The present
tense described a card still being weighed; that commit's changeset says
the key-set refusal "is untouched and is not widened — a hook that
assigns the same key with per-row values still passes it".
- **「Before objectstack-ai#14744」 / 「before objectstack-ai#14744」** at `:686`, `:705`, `:738`,
`:924` (the word 「Before」 sits at the end of the line above at `:685`
and `:704`) became 「before commit 4f85e4d」: before that commit the
flow-facing record shared its nested values with the payload, which is
the reading each sentence quotes.
- **「objectstack-ai#14744 made」, 「objectstack-ai#14744 carries the fix」, 「objectstack-ai#14744 closed the door」**
at `:47`, `:95`, `:642`, 「Until objectstack-ai#14744」 at
`record-change-trigger.ts:341`, 「and objectstack-ai#14744.」 at `:124`, 「objectstack-ai#14744 —
DECOUPLE」 at `:453`, 「(unchanged by objectstack-ai#14744 —」 at `:496`: the number
became the commit, and each sentence already states what the commit did.

## The 4 sites left

- **Test strings, 4 sites on 4 lines**, all `describe` / `it` titles
carrying `objectstack-ai#14744`, left as stages 1 to 12 left theirs:
`before-update-flow-payload-reach.test.ts:825` and `:872` (the second
number of `[objectstack-ai#15356/objectstack-ai#14744]`, a spelling the gate's grammar cannot see),
`decouple-flow-record.test.ts:78` and `:136`.
- No source string, operator log string, assertion message, quoted
maintainer ruling or generated file in this package carries a dead
number.
- Outside `src`, the package's `CHANGELOG.md` names `objectstack-ai#14744` on 2 lines
(467, 478). It is release-owned and deliberately not edited here (see
Acceptance notes). The package `README.md`, which also ships, names none
of the three.

## Mechanical guard: no code token moves

The guard compares, base `91e8fa194` against head, over all 5 touched
`.ts` files:

- **Reading 1**, the TypeScript parser's leaf nodes (a `forEachChild`
walk, so comments are trivia and JSDoc nodes are never visited). String
and template literals are therefore read in full.
- **Reading 2**, the full token stream in parser context (a
`getChildren` walk, so punctuation and keywords are included; JSDoc
nodes skipped).

Results:

- Real run at the final head `bbfe7cb24`: 6,110 base leaf tokens, **0
files with a token change** on either reading (exit 0).
- Comment control in `record-change-trigger.ts` (「reach nothing outside
its own run.」 to 「reach nothing beyond its own run.」): 0 files changed,
as expected (exit 0).
- Positive control, a code token added in `record-change-trigger.ts`
(`params: isolatedRecord,` given `as typeof isolatedRecord`): DIFFER,
953 to 954 leaf tokens and 2,130 to 2,133 full tokens (exit 1).
- Positive control, one digit changed inside a kept test title
(`decouple-flow-record.test.ts:78`, `objectstack-ai#14744` to `objectstack-ai#14745`): 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 (`f3235a962fc5`, `9a8bf70abbcc`), with `git diff HEAD`
empty and a clean tree afterwards.

## Changeset

This change ships bytes, so a `patch` changeset for
`@objectstack/trigger-record-change`
(`.changeset/20596-trigger-record-change-provenance-anchors.md`) is
included. Its body is stage 12's, word for word, with the package name
changed.

Measured on the built package (A3), after a full workspace build in
which this package was a cache miss: `files[]` is `dist`, `README.md`
and `CHANGELOG.md`, and the package is not private.

- `4f85e4d11` appears 3 times in each of `dist/index.js` and
`dist/index.mjs`: the `buildContext` docblock
(`record-change-trigger.ts:340` and `:341`) and the inline comment at
`:496`, which the bundle keeps.
- It appears twice in each of `dist/index.d.ts` and `dist/index.d.mts`:
the same `buildContext` docblock.
- The other three anchors appear nowhere in `dist`: their lines are in
test files. The rewrites at `record-change-trigger.ts:124` and `:453`
and `decouple-flow-record.ts:5` are stripped by the bundle.
- Positive controls, one unchanged line beside each rewrite, land
exactly where their neighbours do: the line after `:341` once in all
four files, the line before `:496` once in each JS file and 0 in the
declaration files, and the neighbours of the three stripped rewrites 0
everywhere.
- A never-written negative phrase appears nowhere in `dist`.
- None of the three dead numbers is left in `dist`.

## Gates (final head `bbfe7cb24`)

- **Citation judging, as CI runs it:** `pnpm check:issue-citations`
exits 0 (self-test, 114 cases, 8 batteries). `node
scripts/check-issue-citations.mjs` exits 0: the diff-scoped run judged 1
added citation across 2 files, the live `objectstack-ai#15356` at
`decouple-flow-record.ts:5`, and it resolves.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0 (the
sibling-package prose-id baseline holds, no growth).
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `bbfe7cb24` (after a fresh fetch)
derived 59 commands. They are all 53 derived at dispatch, plus
`check:engine-double-contract`, `check:objectql-double-limit`,
`check:query-options-erasure`, `check:type-check-coverage`,
`check:type-check-debt` and `check:where-matcher`.
- Each ran with its exit code captured before any pipe, and all 59 exit
0; none exited 3.
- `--ran`, fed each command with its exit code, reports 59 run, 0 NOT
MEASURED (a derived zero), 0 unrun, and exits 0.
- A full `turbo run build` of `./packages/*` and `./packages/*/*` ran
first under the shared verify lock (71 of 71 tasks, exit 0), so no gate
hit an unbuilt workspace.
- **Roster families the derivation lists outside its commands** (their
rosters sit in directories this diff touches): `node
scripts/check-changeset-fixed.mjs`, `pnpm check:authz-resolver`, `pnpm
check:error-code-casing` and `pnpm check:filter-alias-parity`, each exit
0.
- **Tests and typecheck, under the verify lock, at `bbfe7cb24`:**
- `pnpm --filter @objectstack/trigger-record-change test`: 10 files pass
and 101 tests pass. `vitest list --filesOnly` names 10 files, all the
tracked test files, the 3 touched ones included.
- `pnpm --filter @objectstack/trigger-record-change typecheck` exits 0.
`tsc --listFiles` on `tsconfig.test.json` holds all 14 files under
`src/`, and on `tsconfig.json` the 4 non-test files, so all 5 touched
files are compiled.
- **Lint, as a proven narrowing:** eslint with inline config disabled,
over the 5 touched `.ts` files, gives 5 files, 0 errors and 0 warnings
(its `--format json` output). All 5 are in eslint's own population
(`isPathIgnored` is false for each; a `dist` file, as the control, is
ignored). `eslint.config.mjs` never enables type-aware linting (no
`parserOptions.project`, as its own lines 327-328 state), so a comment
edit here cannot move the verdict on any untouched file. The repo-wide
`pnpm lint` is CI's run.
- **Control bytes:** `pnpm check:nul-bytes` exits 0, and a raw scan of
the 6 changed files for control bytes finds none.

## Acceptance notes

- **The gate-invisible spellings, grepped as the claim asked.**
`CITATION_RE` refuses a hyphen after the digits and a `/` before the
`#`, `NON_CITATION_HEADS` excuses a number after the word 「option」, and
a URL-spelled link carries no `#` at all (objectstack-ai#20636). In this package, at
the base and at the head: `#N-word` none, `#A/#B` 5 lines, `option #N`
none, URL-spelled none, which is the claim's 0 / 5 / 0 / 0. Of the five
`#A/#B` second numbers (`objectstack-ai#4251` twice, `objectstack-ai#5038`, `objectstack-ai#4649`, `objectstack-ai#14744`), only
`objectstack-ai#14744` is dead, and it stands in a kept test title.
- **`CHANGELOG.md` is left.**
`packages/triggers/trigger-record-change/CHANGELOG.md` names `objectstack-ai#14744` on
2 lines. It is release-owned (AGENTS.md, Documentation Guardrails), a
deferred surface of the citation gate, and ⛔ not part of this stage.
- **A live number in a runtime string, left for its lane.**
`record-change-trigger.ts:239`'s operator `warn` for an array-form
trigger event ends with the live `objectstack-ai#3457`, and two tests assert the
message carries it. That is form D, not this card's comment-only form C,
and the shrink-only `doc-authoring-prose-id` baseline already holds it
(`record-change-trigger.ts`: `objectstack-ai#3457: 1`), so `check:doc-authoring` sees
no growth.
- **「The card」 phrases are left.** 3 other comment lines in 2 files of
this package speak of 「the card」. They carry no number, neither
instrument sees them, and none of them lost a referent in this diff.
They are unchanged, as in stages 8 to 12.
- **The census instrument did not truncate in this stage.** All three
enumerations read 187 pages at the newest frontier.
- **Anchors the next stages can reuse**, each checked here: `objectstack-ai#14744` →
`4f85e4d11` (the decoupling) or `03c1b0f6f` (its census), both new to
the sweep; `objectstack-ai#13657` → `b003cf2e8` and `objectstack-ai#11081` → `c28e4cfae` reuse the
runtime and lint stages' anchor and stage 7's.
- **Base.** The branch is on `main` at `91e8fa194`. `main` has since
moved six commits (`cd6d8a5ff`, `1bcba27d2`, `a3d7588b5`, `9ad654487`,
`274e16271`, `085ca6bc1`). Their 50 files touch nothing under
`trigger-record-change`, nor `scripts/check-issue-citations.mjs`,
`.changeset/config.json` or the `doc-authoring-prose-id` baseline, and
none is a path in this diff. Three of them are gate inputs
(`scripts/engine-double-contract.pinned.json`,
`scripts/objectql-double-limit.baseline.json`,
`scripts/sdui-manifest.record.json`), so those families ran here against
the base's copies; this diff moves no code token, so nothing here can
interact with them. No merge was taken; the merge queue rebuilds on the
merged generation.

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

---------

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 size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants