Skip to content

docs(trigger-record-change): re-anchor the dead tracker citations to the commits that decided them - #20789

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

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20596-trigger-record-change-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #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 #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 #19123), by the method of stages 1 to 12 (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, PR #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 #14744);
  • 23 sites in test comments, which the census defers: 17 more of #14744, 1 of #13657, and 5 of #11081. #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 #15356 (3 times) and #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 #20779 (newest #20778 before, #20779 after) 802 6 6 2 1
after bb9d39a87 (the comments commit), run 05:07:54Z to 05:11:48Z enumerated, 187 pages, frontier #20780 (newest #20779 before, #20780 after) 796 0 0 0 0
after, final head head bbfe7cb24, run 05:39:10Z to 05:42:26Z enumerated, 187 pages, frontier #20784 (newest #20783 before, #20784 after) 796 0 0 0 0

The before count matches the seat's census and A1 (6 sites, all #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. #11081 answers 404; #5715 and #17982 answer 200 as pull requests; #5785 and #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 /#3457/ regex literals in assertions (live), and two PD #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
#14744 27/4 22/4 4f85e4d11 (PR #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 #14744 in its own words, its diff names #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
#14744 (the census line) (in the row above) 1/0 03c1b0f6f (PR #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 #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
#13657 1/1 1/0 b003cf2e8 (PR #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 #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)
#11081 5/1 5/0 c28e4cfae (PR #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 #11081, and its diff writes the five [#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 #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

The 4 sites left

  • Test strings, 4 sites on 4 lines, all describe / it titles carrying #14744, left as stages 1 to 12 left theirs: before-update-flow-payload-reach.test.ts:825 and :872 (the second number of [#15356/#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 #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, #14744 to #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 #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 (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). 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 (#4251 twice, #5038, #4649, #14744), only #14744 is dead, and it stands in a kept test title.
  • CHANGELOG.md is left. packages/triggers/trigger-record-change/CHANGELOG.md names #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 #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: #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: #14744 → 4f85e4d11 (the decoupling) or 03c1b0f6f (its census), both new to the sweep; #13657 → b003cf2e8 and #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

…the commits that decided them

29 comment and docblock lines in 5 files under
packages/triggers/trigger-record-change/src cited tracker numbers that
answer 404. Each now cites the commit that decided what the line
describes and says so in its own words:

- the flow-facing record decoupled from the batch payload -> 4f85e4d
- the census of same-key / per-row-value beforeUpdate rewrites -> 03c1b0f
- the post-hook half of the declared-field door -> b003cf2
- the shared expected read-refusal noise capture -> c28e4cf

Comments only: every file keeps its line count and no code token moves.
Test titles that carry a dead number are strings and are left.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
…nchoring

The rewritten buildContext docblock and one inline comment ship in dist,
so the package takes a patch changeset.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
The paragraph that now opens with the census commit said the source
reading was recorded "on that card"; that census record carries the
reading itself, so the line now says so.

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

This PR changes 1 package(s): @objectstack/trigger-record-change, touching 2 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/triggers/trigger-record-change/src/decouple-flow-record.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

⛔ 2 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via RecordChangeTrigger (symbol, a top-level class))
  • content/docs/releases/v17/17-4.mdx (via buildContext (symbol, a method of class RecordChangeTrigger))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/triggers/trigger-record-change/src/decouple-flow-record.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 1 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 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 263425d70bfdae2a07837418e10d63b018804a09 — the merge of head bbfe7cb244fb2ec1bf78aa15eb437d6b8093f9f8 into base 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb, 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 263425d70bfdae2a07837418e10d63b018804a09 && git checkout 263425d70bfdae2a07837418e10d63b018804a09
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb bbfe7cb244fb2ec1bf78aa15eb437d6b8093f9f8 && git checkout -B drift-repro 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb && git merge --no-ff bbfe7cb244fb2ec1bf78aa15eb437d6b8093f9f8

node scripts/docs-audit/affected-docs.mjs --json 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 4b4ee88fbc530a155ce3ea0570ad81d905d3afbb → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bbfe7cb244fb2ec1bf78aa15eb437d6b8093f9f8
Local-runs: none

① Derived judgments

Read against main at the merge-base 91e8fa194 (stage 12's landing), board and tree read 2026-09-30T06:30Z. The PR's recorded base is 4b4ee88fb; origin/main at this read is 7a09eee1b, eight commits past the merge-base (cd6d8a5ff, 1bcba27d2, a3d7588b5, 9ad654487, 274e16271, 085ca6bc1, 4b4ee88fb, 7a09eee1b). None of the eight touches packages/triggers/trigger-record-change/**, this PR's changeset, .changeset/config.json, scripts/check-issue-citations.mjs or scripts/doc-authoring-prose-id.baseline.json, so the net diff against main is the merge-base diff, and the three-dot and two-dot numstats are byte-identical: 6 files, +40/−30 — 5 source files under packages/triggers/trigger-record-change/src/** (2 modules, 3 test files) and one changeset. The head repo is the base repo.

  • Accept-set: no change — right. No Zod schema, REST handler, query-parameter set, refusal text, log text or runtime string moves. 30 source lines out, 30 in; every one of the 60 changed lines opens with a comment marker after whitespace (//, *, /**), 0 fall outside one. Each of the 5 touched files keeps its line count at the head (932, 219, 142, 861, 643), so no line citation into these files moves. The dev's parser leaf-token guard (0 files with a token change; both positive controls DIFFER) says the same and is not repeated here.
  • Public surface: no change — right. No export added, removed or renamed; no packages/spec file touched, so no generated artifact is owed.
  • Published bytes: changed — right, and it decides ②. @objectstack/trigger-record-change (17.5.0, not private, files = dist, README.md, CHANGELOG.md, types = dist/index.d.ts, built by the root tsup.config.ts with dts on, then check-dts-emitted). src/index.ts exports RecordChangeTrigger, and the rewritten buildContext docblock (record-change-trigger.ts:340-341) is the leading comment of a member of that class, so the declaration emit carries the new text. The dev's A3 reading over all four dist entries, with positive and negative controls (the sha 3 times in each JS entry, 2 in each declaration file; no dead number left in dist), says the same; this record does not repeat the build.
  • The 3 numbers are dead — right. #14744, #13657 and #11081 each answer 404 on the issues endpoint, read for this record; #15356 and #8738, the only two numbers kept on added lines, answer 200. No ADR, scripts/adr-anchors/ file or ruling record names any of the three as its decision (the dev's grep; the one docs/ page naming #14744 is the census record, which calls itself measurement only), so ruling C's first rung is empty and a commit is the right anchor for every one.
  • The 4 anchors — each right. Each abbreviated sha resolves to exactly one commit (rev-parse --disambiguate, count 1 for all 4) and each is an ancestor of the merge-base (merge-base --is-ancestor, exit 0 for all 4). git blame at the base was run over every changed line: the 25 lines in before-update-flow-payload-reach.test.ts, decouple-flow-record.test.ts, decouple-flow-record.ts and record-change-trigger.ts were all written by 4f85e4d11, and the 5 lines in record-change-integration.test.ts were all written by c28e4cfae.
  • The wordings — each right. :10 names the option-A ruling in words beside the commit whose changeset records it (the form stages 2, 6 and 7 used). :22 and :87 say the residue shape is what 4f85e4d11 pins: the positive control is in the test file that commit created. :455 says the blind spot is what 4f85e4d11 "left unguarded": its changeset says the key-set refusal is untouched and not widened. :29 and :34 (the census, and the conclusion recorded in it) are checked above. :686, :705, :738 and :924 ("before commit 4f85e4d … read …") state the pre-fix readings the commit's own flip of S5, S5b and the SQL replica reversed. :206 is checked above. :23, :47, :95, :642, record-change-trigger.ts:124, :340-341, :453 and :496 swap the number for the commit in a sentence that already states what the commit did.
  • Citation accounting — right. Over the diff: the 30 removed lines carry #14744 ×23, #11081 ×5 and #13657 ×1 (29 dead sites on 29 lines) plus the live #15356 ×3 and #8738 ×1; the added lines carry exactly #15356 ×3 and #8738 ×1, each on the line it already stood on. No number is new to an added line, none grew, no PR #N stands on an added line, and 4 distinct shas stand on added lines: 4f85e4d11 on 22 lines, c28e4cfae on 5, 03c1b0f6f on 1, b003cf2e8 on 1 — 29 rewritten lines, plus the one referent line (:34), which is the thirtieth.
  • The 4 sites left — right, and the list is exact. A grep of the three numbers over trigger-record-change/src at the head returns exactly 4 lines: before-update-flow-payload-reach.test.ts:825 (an it title), :872 (a describe title, the second number of [#15356/#14744]), decouple-flow-record.test.ts:78 and :136 (describe titles). All four are string tokens, left as stages 1 to 12 left theirs. No operator string, assertion message, generated header or quoted ruling under src carries a dead number.
  • The gate-invisible spellings — right. At the head under src: #N-word 0; #A/#B 5 lines (before-update-flow-payload-reach.test.ts:118 #4127/#4251, :872, bulk-write-per-row-context.test.ts:53 #4127/#4251, :188 #4862/#5038, record-change-trigger.ts:388 #1871/#4649), of whose second numbers only #14744 is dead and it stands in the kept title; option #N 0; URL-spelled 0. The claim's 0 / 5 / 0 / 0.
  • Form — consistent with the landed stages 1 to 12 (422db788a … 91e8fa194): the word commit plus the abbreviated sha where the number stood, the decision carried in the sentence. The two reuses this thread can check, #13657 → b003cf2e8 and #11081 → c28e4cfae, are the anchors the earlier stages named for the same numbers.
  • Check-runs on the head, the gate verdicts, read 2026-09-30T06:30Z: 34 check-runs, all completed — 31 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in): paths-filtered or opt-in, not verdicts against), 0 failure. Every one of the seven required contexts is 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 (all six shards success), Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. Check Changeset, Check PR Size, Part-of PR must not also close its card, The card this PR closes must claim this branch, No other open PR may claim the same issue and No other open PR may claim the same single-writer path are success. Nothing was built, run or re-run locally.

② Semver level

  • .changeset/20596-trigger-record-change-provenance-anchors.md declares '@objectstack/trigger-record-change': patch — matches what the diff publishes. The package is released and its dist/index.d.ts carries the rewritten buildContext docblock, so bytes ship; skip-changeset would be wrong (it is for a diff that publishes nothing from any released package), and the PR carries no such label (documentation, size/s, tests, tooling, the labeler's). Not minor: no accept set widens and no surface is added. The body is stage 12's word for word except the package name (a diff of the two files changes exactly the two lines that name it), is truthful (comments only; no type, schema, export, log or refusal text, or runtime behaviour change), carries no tracker number and no model identifier, and the filename carries the card number. The package sits in the fixed group (.changeset/config.json:51) beside the eleven packages whose stage changesets are still in .changeset/ on main, all declaring the same level.
  • Clause-②: no — right. It is line 2 of the PR body under Part of #20596, and the claim (5904332626) 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; Check Changeset on the head is success.
  • Not a governed-surface diff (no path under docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md); 70 changed lines, under the 5,000-line human-merge threshold; head repo equals base repo; Governed Surface Queue Guard on the head is success. A draft with Part of on line 1 and no closing keyword anywhere in the body, so the card stays open for the remaining stages.

③ Boundary flags

The dev report (5905231194) has open_questions: []. Its twelve deviations and three out-of-scope findings, each answered, then one reading of this record's own:

  1. 23 test-comment sites beyond the census's 6, five of them #11081 which the census never judged — answered, in scope. The claim's surface is comment and docblock prose under trigger-record-change/src/**; test comments are that, and stages 1 to 12 rewrote theirs. #11081 answers 404 on this record's own read, and the head grep above confirms the residue is titles only.
  2. #14744 takes two anchors, per line — answered, right (① above). The census line cites the census commit and the 22 fix lines cite the fix; form C applied per line rather than per number, the per-arm precedent of stages 5 and 9 on this thread.
  3. One changed line with no dead number (before-update-flow-payload-reach.test.ts:34), landed as its own commit bbfe7cb24 — answered, right. Its referent moved with :29; the census record 03c1b0f6f landed carries the conclusion the line now points at (:308-311, blamed to that commit). Line count unchanged; the head's check-runs are on this commit.
  4. Wordings beyond the tag swap — answered, right (① above). Each was checked against its anchor commit's message, diff or changeset.
  5. The supplementary instrument judged by stage 6's method (the before census's board reading, the gate's own extraction, single-number reads) — answered, immaterial here. A process note; the head grep of the three numbers is the residue check this record reads, and it is exact.
  6. The harness attribution reminder versus the repo's trailer pair — answered, right. All three head commits (bb9d39a87, bf03049f3, bbfe7cb24) end with the model-free trailer pair AGENTS.md prescribes (the session-URL trailer and the plain co-author trailer), no model identifier appears in any message, and the PR body's footer is the session-URL form. AGENTS.md is the repo's rule, and the reminder itself defers to it.
  7. No pre-PR merge; main moved past the base — answered, right. Verified above at eight commits: none touches a path in this diff or an input the citation gate derives from. The three roster inputs that moved (engine-double-contract.pinned.json, objectql-double-limit.baseline.json, sdui-manifest.record.json) cannot interact with a diff that moves no code token, and the merge queue rebuilds on the merged generation.
  8. A scratch eslint probe placed, run and deleted in one command — answered, immaterial. The head tree holds no such file (git ls-tree -r, 0 hits).
  9. Three census enumerations rather than two — answered. More evidence, not less; the final one is on the head.
  10. Labels — answered. The labeler's four; no skip-changeset, which is right.
  11. The PR body read back through the API but not byte-compared — answered, immaterial. The body as this record read it is complete: every section present, one footer, Part of #20596 line 1, Clause-②: no line 2.
  12. Worktree cleanup after the report — answered. A process note with no bearing on the head.
  13. Out-of-scope 1, record-change-trigger.ts:239's operator warn ending in #3457 — answered. The number is live; form D, already held by the shrink-only scripts/doc-authoring-prose-id.baseline.json (record-change-trigger.ts: #3457: 1), so check:doc-authoring sees no growth. Carrier: the runtime-string lane.
  14. Out-of-scope 2, 「the card」 on 3 comment lines in 2 files — answered, verified at the head. before-update-flow-payload-reach.test.ts:865 sits under the heading at :859, whose surviving number #15356 is the card whose consequence the line names (one SET clause, issued once, landing on every row — that card's own text), so the referent stands; reentrant-start-condition.test.ts:14 and :302 are untouched by this diff. Wording only.
  15. Out-of-scope 3, docs/audits/** names the dead #14744 on 8 lines in 2 files — verified at the head; escalated to its carrier, not to this PR. docs/ is outside every stage of this card, and the census file names the number as its own subject. A dead citation in an audit record is documentation rot, not a defect or contract violation. Its carrier is [finding] dead tracker citations outside packages/spec/src have no carrier: #20234 sweeps only the spec tree, and PR #20554 makes 26 more visible (pre-#N / Pre-#N) in cli, drivers, metadata, objectql, plugins, runtime and types #20556 (dead citations outside packages/spec/src), where stage 2's record already sent an ADR finding of the same class; the seat should leave a pointer there on its next write, with 03c1b0f6f as the anchor already checked here. Nothing for the dev.
  16. This record's own reading, not in the PR body or the report: packages/triggers/trigger-record-change/tsconfig.test.json:54 carries [#11081] in a // comment (the line explaining why rootDir is widened). It is outside the claim's file surface (src/** only), outside the census's declared surface (packages/**/src/**/*.{ts,tsx}), and not in files[], so it neither ships nor moves any gate, and leaving it is not a breach of the dispatch. It is a dead citation in this package all the same, and the body's outside-src inventory (which lists the shipping files only) does not name it. Escalated as a pointer, not as a flag on this PR: the carrier is this card's thread, for the seat's landing note or a later stage in this lane, with c28e4cfae as the anchor already checked here — its message names #11081 and names this very rootDir widening. Not blocking.

Nothing else is escalated.

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

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 06: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 8acdae9 Sep 30, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20596-trigger-record-change-citations branch September 30, 2026 06:52
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