Repository navigation
docs(formula): re-anchor the dead tracker citations to the commit that decided them (stage 12 of #20595) - #21635
Conversation
…t decided them Six comment and docblock sites in packages/formula cited a tracker number that now answers 404 (four census sites in src/cel-engine.ts, two test comments in src/validate.test.ts). Each now cites commit e9b5265, the change that added current_user to SCOPE_ROOTS and moved the field-level rejection into its own rule. Comment text only; no line count changes. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
Two of the four rewritten cel-engine.ts lines reach the published dist (the SCOPE_ROOTS docblock in the declarations, one kept comment in the JavaScript), measured with a three-leg dist reading. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 8 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cf96ff502106cbbeefea4ee201e519182028cdd4 && git checkout cf96ff502106cbbeefea4ee201e519182028cdd4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a4f0cb0a45b2b3841947520c896037e122d1c2a1 b89eb86cbe1542c7adea98424516f17ca3a10a70 && git checkout -B drift-repro a4f0cb0a45b2b3841947520c896037e122d1c2a1 && git merge --no-ff b89eb86cbe1542c7adea98424516f17ca3a10a70
node scripts/docs-audit/affected-docs.mjs --json a4f0cb0a45b2b3841947520c896037e122d1c2a1 |
ACCEPT — PR #21635 at head
|
… that decided them (stage 13 of objectstack-ai#20595) (objectstack-ai#21640) Part of objectstack-ai#20595 Clause-②: no ## What changed Stage 13 of the `domain:engine` lane of the dead-citation sweep: `packages/metadata-fs/**`, comment and docblock prose only, per the claim (`5973452558`). Stages 1 to 11 landed as `a7d9768ec`, `d150c3039`, `4bf4e7e70`, `13a24ece2`, `db0cf2231`, `85986144c`, `48fa7a381`, `c205b6c35`, `c98a72d69`, `fd5a1cd59` and `f97660cdd`; stage 12 (`packages/formula`) is PR objectstack-ai#21635, in review, with a disjoint file surface. objectstack-ai#20595 stays open: this PR does not touch `formula`, nor the test-string sites the card carries for a widened stage, and the seat decides the card's close-out. Every comment or docblock site in the package that cited a tracker number answering 404 is rewritten in ruling C+D's form C (record `5749154545` on objectstack-ai#19123): the ADR when one records the decision, otherwise the commit in this repository's history that made it. That is **3 sites on 3 lines in 3 files, all citing one number (objectstack-ai#11021), all re-anchored to one commit, `7d81c889f`**: - **2 census sites** (2 lines: `src/repository.ts:230` and `src/sync.ts:41`): the whole `allocated-but-absent` population of the gate's own census in this package at the base; - **1 test-comment site** (`test/close-terminates-watch.test.ts:103`), outside the census glob (`test/` is not under `src/`, and the census defers test files anyway). Same number; - **outside the census glob, inside the claimed surface: none.** `README.md`, `tsconfig.test.json` and `vitest.config.ts` carry 10 citations between them, and all 10 resolve on the enumerated board (below); `package.json`, `tsconfig.json` and `tsup.config.ts` carry none; - **dead comment ids: none.** The package carries no ten-digit comment id and no `issuecomment` or `discussion_r` link (git grep exit 1). **Anchors: 1 number, by commit; 0 by ADR, 0 by repository qualifier; 1 sha.** `7d81c889f` is the anchor stage 1 (`a7d9768ec`) and stage 9 (`c98a72d69`) already chose for the same number; it is reused here and re-proven below for this package's sentences. Only comments changed. All three files keep their line counts (3 lines out, 3 in, plus the changeset), so no line citation into any of them moves. No code token moves (the guard below). All 6 changed lines under `packages/` open with a comment marker. **No citation number is added**: the only tracker number on a `+` line is `objectstack-ai#11127` on `sync.ts:41`, carried over unchanged from the `-` line, and it resolves (a closed issue). The only new nine-hex span is `7d81c889f`, 3 times. **A `patch` changeset**: 1 of the 2 rewritten non-test lines is in the published `dist` (the `FileSystemRepository.close()` docblock, in the declarations and in the JavaScript esbuild emits), and `dist` is not byte-identical with the base text (see Changeset). ## H0: the package and its size The gate's own `node scripts/check-issue-citations.mjs --census --json` at base `0fc80878f` (the before run below), `allocated-but-absent` per remaining `domain:engine` package (stage 12's H0 list): | package | before | after this stage | |---|---|---| | `metadata-fs` | **2** | **0** | | `formula` | 4 | 4 (removed by stage 12, PR objectstack-ai#21635, not yet on `main`) | | `drivers/driver-mongodb`, `drivers/driver-turso`, `metadata-core`, `core`, `metadata-protocol`, `objectql`, `metadata`, `drivers/driver-sql`, `drivers/driver-memory`, `drivers/driver-sqlite-wasm`, `plugins/plugin-pinyin-search`, `platform-objects` | 0 each | 0 each | `metadata-fs` reads 2, both objectstack-ai#11021, at `repository.ts:230` and `sync.ts:41`, exactly as stage 12's head census (`b89eb86cb`) read it. So the stage went ahead. On this branch's tree the lane total goes 6 to 4; with stage 12's PR it is 0. ## Census: `metadata-fs`, before and after **Instrument.** The gate's own `node scripts/check-issue-citations.mjs --census --json`, read-only and unchanged. The count is its `allocated-but-absent` findings under `packages/metadata-fs/`. | reading | tree | board | whole-repo `allocated-but-absent` | sites | lines | files | numbers | |---|---|---|---|---|---|---|---| | before | base `0fc80878f`, run 21:04:24Z to 21:07:44Z | enumerated, 195 pages, frontier objectstack-ai#21636, 19,457 records | 119 | **2** | 2 | 2 | 1 | | after | `67f182575`, run 21:14:26Z to 21:17:43Z | enumerated, 195 pages, frontier objectstack-ai#21637, 19,458 records | 117 | **0** | 0 | 0 | 0 | | head | `32de59091`, run 21:37:42Z to 21:40:56Z | enumerated, 195 pages, frontier objectstack-ai#21639, 19,460 records | 117 | **0** | 0 | 0 | 0 | The whole-repo drop is 2, and the before and after finding sets differ by exactly the 2 rows of this package, removed; none was added. `resolves` (35,763), `resolves-as-pull-request` (2,383) and `cross-repo-unjudged` (1,254) did not move: `objectstack-ai#11127` stays on `sync.ts:41` and still resolves. The head's only later commit is the changeset; the head run's finding set is identical to the after run's, line numbers included. **Supplementary instrument, the whole package.** The census reads neither test files nor strings nor files outside `src`. A second reading runs the gate's own exported `extractCitations` (whole-file and comment-prose projections) over every tracked file in the package (24) and classifies each citation with the gate's `classifyCitation` against one board enumerated by the gate's `enumerateBoard` (195 pages, frontier objectstack-ai#21636, 19,457 records, read from 21:09:16Z to 21:12:31Z), the same board for both readings. | reading | citations | dead | src comment | test comment | test string | other files | changelog | |---|---|---|---|---|---|---|---| | before, `0fc80878f` | 137 | **5** | 2 | 1 | 0 | 0 | 2 | | after, `67f182575` | 134 | **2** | 0 | 0 | 0 | 0 | 2 | The citation count drops by 3, the 3 rewritten sites; no respelling stays a citation. The live counts did not move (src comment: 39 resolve as issues, 2 as pull requests; test comment: 44 and 9; test string: 10 and 0; files outside `src` and `test`: 8 and 2; changelog: 16 and 2). A third, raw reading (every `#` followed by 2 to 6 digits, whatever surrounds it, `CHANGELOG.md` aside) counts 117 before and 114 after: also a drop of 3. **Single reads** over the issues endpoint: objectstack-ai#11021 answers 404; objectstack-ai#11127 answers 200 (a closed issue: `FileSystemRepository.close()` never reaching its broker); objectstack-ai#11136 answers 200 (the pull request whose squash is `7d81c889f`). ## Per-number table `src` counts census sites, `test` the test-comment sites. | number | src | test | anchor | kind | source | what it decided | |---|---|---|---|---|---|---| | `objectstack-ai#11021` | 2 | 1 | `7d81c889f` | commit | reused (stage 1 `a7d9768ec`, stage 9 `c98a72d69`), re-proven here | `close()` terminates watch iterators instead of emitting a drain event: it measured why a synthetic drain event is the wrong shape (the filter and the numeric `since` drop it, and delivering an event never ends an iterator), fixed that defect in `SysMetadataRepository`, and wrote invariant 8 in `metadata-core`'s `repository.ts` (the squash of PR objectstack-ai#11136) | Each sentence, judged against the decision it describes rather than the number: - **`repository.ts:230`** says a synthetic drain event 「would be the wrong shape and was measured to be so」. The measurement is `7d81c889f`'s: its message and its invariant 8 text record both halves (the filters drop it; delivery never ends an iterator). The `metadata-fs` fix that wrote this docblock, `46644e25a` (objectstack-ai#11127), cites objectstack-ai#11021 as that earlier decision. - **`sync.ts:41`** sends the reader to invariant 8 in `metadata-core`'s `repository.ts` and names the pair `(objectstack-ai#11021, objectstack-ai#11127)`. `git blame` at the base puts invariant 8's statement (`repository.ts` :53 to :76) on `7d81c889f` (its line :63 since re-anchored by stage 9), and its conformance rows for `FileSystemRepository` and `InMemoryRepository` (:77, :79 to :88) on `46644e25a`, the objectstack-ai#11127 fix. So the dead half of the pair becomes `commit 7d81c88`, and the live `objectstack-ai#11127` stays. - **`close-terminates-watch.test.ts:103`** contrasts this package's hang with 「the sibling defect」: the filter-dependent drain-event drop in `SysMetadataRepository`, which is the defect `7d81c889f` fixed. The proof, per the earlier stages' standard: - `git rev-parse --disambiguate=7d81c889f` matches exactly one commit, `7d81c889f1f190a273e59ee584b7522ca6a792fa`. - `git merge-base --is-ancestor` puts it under the base `0fc80878f`, under `origin/main` `a1ca156da` at the time, and under `main` `5b5e83f44` fetched later (exit 0 each; exit 0 is self-proving, and the repository is not shallow). - It names objectstack-ai#11021 in its message (the subject) and in its diff (7 diff lines). - `git blame` at the base puts all 3 changed lines on `46644e25a`, which cites objectstack-ai#11021 as the sibling decision; that decision is `7d81c889f`'s diff, as the three bullets above set out. - No ADR names objectstack-ai#11021 or the drain-event decision (git grep over `docs/adr` for the number, 「drain event」 and 「Shutdown terminates」, exit 1). ## Wordings to check All 3 rewrites swap a tag in place, in forms the earlier stages already use: - `(objectstack-ai#11021)` became `(commit 7d81c88)` at `repository.ts:230` (stage 1's `(#N)` form). - `(objectstack-ai#11021, objectstack-ai#11127)` became `(commit 7d81c88, objectstack-ai#11127)` at `sync.ts:41`. The mixed commit-and-issue pair already stands in `packages/cli` (`(commit 44813ba, objectstack-ai#14554)`). - 「unlike the sibling defect in objectstack-ai#11021」 became 「unlike the sibling defect commit 7d81c88 fixed」 at `close-terminates-watch.test.ts:103`, the form `packages/cli/src/commands/generate-multiple-json-column.pin.test.ts:57` uses (「the defect commit ee370d3 fixed」). **No reflow.** No line was reflowed, so `repository.ts:230` and `close-terminates-watch.test.ts:103` are now longer than their block's wrap. `eslint.config.mjs` declares no line-length rule, and a reflow would move neighbouring lines. No file cites a line of any of the three touched files (git grep exit 1), and neither changed phrase is quoted elsewhere. ## Sites left - **In comments (src, test, outside the glob): none.** - **String literals: none.** The package's 10 test-string citations all resolve. - **Outside `src` and `test`:** the release-owned `CHANGELOG.md` names objectstack-ai#13112 (line 47) and objectstack-ai#11021 (line 196), both answering 404; left. ## Mechanical guard: no code token moves The guard compares base `0fc80878f` against the tree over all three touched files, with TypeScript 6.0.3, to the earlier stages' two-reading specification. The earlier guard scripts were scratch files, so it was rewritten here to that specification and proven with the controls below. - **Reading 1**: the parser's leaf nodes, from a `forEachChild` walk. Comments are trivia there, and JSDoc is never visited. A leaf that is not itself a token is re-scanned with trivia skipped. - **Reading 2**: the full token stream in parser context, from a `getChildren` walk, with JSDoc nodes skipped. String, template and numeric literals are compared in full on both readings. Results, at `67f182575` (the later commit touches none of the three files): - Real run: 6,075 base tokens (reading 2), **0 files with a token change** (exit 0). - Comment controls: 「awaiting the watcher,」 to 「… the WATCHER,」 (`repository.ts`), 「Best-effort cleanup:」 to 「Best-effort CLEANUP:」 (`sync.ts`) and 「bites on its own.」 to 「bites ON its own.」 (the test file). 0 files changed (exit 0 each). - Positive control, an identifier (`export function createBroker(` to `createBrokerX(`, `sync.ts`): DIFFER on both readings (exit 1). - Positive control, a string literal (the word 「no」 in the 「empty filter, no since」 row label, to 「NO」, the test file): DIFFER on both readings (exit 1). - Positive control, a template literal (the `@` separator in the sweep-failure key template, to `#`, `repository.ts`): DIFFER on both readings (exit 1). - Positive control, a numeric literal (`SETTLE_MS = 2_000` to `2_001`, the test file): DIFFER on both readings (exit 1). Each mutation went through `scripts/ablation-replace.mjs` (wrap mode; the anchor hit 1 before and 0 after, and the blob changed). It ran under a shell trap that restores by absolute path from `HEAD`. Each restore was proven equal to its `HEAD` blob (`888f39203432`, `122a45dcb516`, `0d266189d60c`), and afterwards `git diff HEAD` was empty and the tree clean. ## Changeset: `patch` (`dist` measured) `files[]` is `dist`, `README.md` and `CHANGELOG.md`, and the package is not private. One script ran under the shared verify lock (VERDICT command-exit 0, held 79s, shared-box seconds), at `67f182575`. It built the dependency closure first (`pnpm --workspace-concurrency=2 --filter '@objectstack/metadata-fs^...' build`, exit 0), then ran the package's own `build` (tsup and `check-dts-emitted`) three times, exit 0 each: - **Leg 1**, the head text: 6 `dist` files hashed (`index.js`, `index.cjs`, their sourcemaps, `index.d.ts`, `index.d.cts`). 1 of the 2 rewritten non-test lines appears verbatim in `dist`: the `close()` docblock line (`repository.ts:230`), in `index.d.ts`, `index.d.cts`, `index.js` and `index.cjs` (esbuild keeps that docblock). The other (`sync.ts:41`) sits on an interface that is erased from the JavaScript and never reaches the declarations (grep exit 1). `scripts/ablation-dist-preflight.mjs` finds the head marker 「was measured to be so (commit 7d81c88)」 in those 4 files with a clean tree (exit 0). - **Leg 2**, the base text put back in `repository.ts` and `sync.ts` (proven equal to their base blobs `d551426a7545` and `48798bcf7661`, written to the tree only, 0 paths staged): 4 of the 6 files differ from leg 1 (`index.js`, `index.cjs`, `index.d.ts`, `index.d.cts`); the two sourcemaps do not. The preflight finds the base marker 「was measured to be so (objectstack-ai#11021)」 in the same 4 files (exit 0). - **Leg 3**, after the proven restore (equal to the `HEAD` blobs `888f39203432` and `122a45dcb516`, `git diff HEAD` empty, porcelain empty): all 6 files are byte-identical to leg 1. The preflight's `--absent` reading of the base marker exits 0 with a clean tree. So the build is deterministic, and the difference is the rewrite. So the rewrite ships. `.changeset/20595-metadata-fs-provenance-anchors.md` declares a `patch` for `@objectstack/metadata-fs`, comment text only, with the claim's `Clause-②: no` line. The anchor is a commit, so the changeset names no ADR, repository qualifier or bracketed substitution. It says which published files carry the reworded text, as measured above. The changeset commit touches no file under `packages/metadata-fs`. ## Gates (head `32de59091`) - **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at `32de59091` (4 paths against merge base `0fc80878f`) derived 61 commands. All 61 ran (21:25:07Z to 21:36:22Z, after the workspace build), each exit code captured before any pipe: 61 exit 0. `--ran` reports 「61 derived, 61 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived zero) and exits 0. - The dispatch's lead derivation (47 commands, one path) is a subset. The extra 14 are: - the eight families the `.changeset/` path adds: the ADR-0087 registration and empty-changeset pairs, `check:objectui-changeset`, `check:pm-changeset-deadline-census` and two release self-tests; - `check:type-check-coverage` and `check:type-check-debt`; - four gates whose sources name the touched files: `check:engine-double-contract`, `check:objectql-double-limit`, `check:query-options-erasure` and `check:where-matcher`. - **Named readings:** - `node scripts/check-issue-citations.mjs` exits 0 (1 citation judged across 2 files: the carried-over `objectstack-ai#11127`, which resolves). - `pnpm check:issue-citations` exits 0 (its self-test, 173 cases, 9 batteries). - `pnpm check:doc-authoring` exits 0 (the sibling-package prose-id baseline holds, no growth). - `pnpm check:nul-bytes` exits 0 (10,001 files, no raw control bytes), and a control-byte grep over the 4 changed files finds none (exit 1). - The changeset gates exit 0: `check-adr-0087-registration` (「1 non-breaking changeset(s) seen」), `check-empty-changeset` (「1 declaring changeset(s) added」), `check-changeset-no-major` (「no `major` bump」), and `check:changeset-gate-self-tests`. The Clause-② level axis of `check-changeset-no-major` reads the pull request body, so it does not apply to a local run; that reading is CI's. - **Build, tests and typecheck, under the verify lock**, at `32de59091`: - The workspace build (`turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2`): VERDICT command-exit 0, held 198s, shared-box seconds; 71 of 71 tasks, 20 cached. - The tests and typecheck: held 33s. `pnpm --filter @objectstack/metadata-fs test`: 10 test files pass, 70 tests pass (exit 0). `pnpm --filter @objectstack/metadata-fs typecheck` (`tsc --noEmit` and `tsc --noEmit -p tsconfig.test.json`) exits 0. - `tsc --listFilesOnly` puts every touched file in a program: `repository.ts` and `sync.ts` in both configs, and `close-terminates-watch.test.ts` in `tsconfig.test.json`, whose program holds all 10 test files. - No importing package owes a run, because the declaration files change only in comment text. - **Lint, as a proven narrowing, at `32de59091`:** - eslint ran with inline config disabled (`--format json`) over the 3 touched files plus `dist/index.js` as the control. - 4 results: 0 errors, and 1 warning, which is the control's ignore notice. No touched file is reported ignored, and `--print-config` resolves a config for each. - `eslint.config.mjs` never enables type-aware linting (its lines 327 and 328 say so; `--print-config` shows no `parserOptions.project` and no `projectService`), so a comment edit cannot move the verdict on an untouched file. - The repo-wide `pnpm lint` is CI's run. ## Acceptance notes - **Base, and no merge.** The dispatch read `origin/main` at `0fc80878f`, and the worktree was cut there. Every reading above is on this branch's own tree. Before this PR was opened, `main` moved 4 commits, to `5b5e83f44` (`objectql`, `driver-sql` and `service-analytics`, `spec`). - None of them touches `packages/metadata-fs`, `check-issue-citations.mjs` or `dispatch-gates.mjs`. - A local `git merge-tree` of the head with `5b5e83f44` is clean (exit 0), and the anchor `7d81c889f` is under it too (`--is-ancestor`, exit 0). - The gate's diff mode, run in a scratch checkout of `5b5e83f44` against `0fc80878f`, judges the 26 citations those 4 commits add: all 26 resolve. So they add no dead citation to the lane's packages. - The branch is not merged with them: the gate derivation reads the three-dot change set against the merge base `0fc80878f`, and CI and the merge queue run on the merged ref. - **History.** The repository is not shallow (`git rev-parse --is-shallow-repository` answers false), so no deepening was needed before the blame, ancestry and history readings. - **The same dead numbers outside this package's comments**, each left to its own carrier: `CHANGELOG.md` (objectstack-ai#13112 and objectstack-ai#11021, release-owned). - **Wording only:** no line without the number was changed. --- _Generated by [Claude Code](https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Part of #20595
Clause-②: no
What changed
Stage 12 of the
domain:enginelane of the dead-citation sweep:packages/formula/**, comment and docblock prose only, per the claim (5973031304). Stages 1 to 11 landed asa7d9768ec,d150c3039,4bf4e7e70,13a24ece2,db0cf2231,85986144c,48fa7a381,c205b6c35,c98a72d69,fd5a1cd59andf97660cdd. #20595 stays open: the lane's other remaining package,metadata-fs(2 census sites after this stage), is not touched here, and neither are the test-string sites the card carries for a widened stage.Every comment or docblock site in the package that cited a tracker number answering 404 is rewritten in ruling C+D's form C (record
5749154545on #19123): the ADR when one records the decision, otherwise the commit in this repository's history that made it. That is 6 sites on 6 lines in 2 files, all citing one number (#6290), all re-anchored to one commit,e9b526597:src/cel-engine.ts:120, :147, :160, :173): the wholeallocated-but-absentpopulation of the gate's own census in this package at the base;src/validate.test.ts:933 and :958), which the census defers by its test-file exclusion. Same number;README.md,package.json,tsconfig.json,tsconfig.test.json,test-typecheck-debt.jsonandvitest.config.tscarry 10 citations between them, and all 10 resolve on the enumerated board (below);5865693155(the ruling record on [Decision] How does a filter say 「is empty」 on a multi-value field? A declared$emptyoperator, or reopen the empty-list refusal (ruling B on #20311, its third arm) #20399, atmatches-filter.ts:808andmatches-filter-empty-operator.test.ts:8), answers 200.Anchors: 1 number, by commit; 0 by ADR, 0 by repository qualifier; 1 sha.
e9b526597is the anchor thepackages/lintsweep (aa23e2c8f) already chose for the same number; it is reused here and re-proven below for this package's sentences.Only comments changed. Both files keep their line counts (6 lines out, 6 in, plus the changeset), so no line citation into either file moves. No code token moves (the guard below). All 12 changed lines open with a comment marker. No citation number is added: the
+lines carry no tracker number at all, and their only new nine-hex span ise9b526597, 6 times.A
patchchangeset: 2 of the 4 rewritten non-test lines are in the publisheddist(theSCOPE_ROOTSdocblock in the.d.ts, and one//line inside that list that esbuild keeps in the JavaScript), anddistis not byte-identical with the base text (see Changeset).H0: the package and its size
The gate's own
node scripts/check-issue-citations.mjs --census --jsonat base045b94625(the before run below),allocated-but-absentper remainingdomain:enginepackage:formulametadata-fsdrivers/driver-mongodb,drivers/driver-turso,metadata-core,core,metadata-protocol,objectql,metadata,drivers/driver-sql,drivers/driver-memory,drivers/driver-sqlite-wasm,plugins/plugin-pinyin-search,platform-objectsThe lane total goes 6 to 2.
formulareads 4, as at stage 11's head census (b69c176e9): the 4 sites, the one number and the one file the dispatch named. So the stage went ahead.Census:
formula, before and afterInstrument (A1). The gate's own
node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count is itsallocated-but-absentfindings underpackages/formula/.allocated-but-absent045b94625, run 20:12:19Z to 20:16:21Z7a520db9d, run 20:22:39Z to 20:25:58Zb89eb86cb, run 20:48:23Z to 20:51:45ZThe whole-repo drop is 4, and the before and after finding sets differ by exactly the 4 rows of this package, removed; none was added.
resolves(35,759),resolves-as-pull-request(2,383) andcross-repo-unjudged(1,254) did not move. The head's only later commit is the changeset; the head run's finding set is identical to the after run's, line numbers included.Supplementary instrument, the whole package. The census reads neither test files nor strings nor files outside
src. A second reading runs the gate's own exportedextractCitations(whole-file and comment-prose projections) over every tracked file in the package (69) and classifies each citation with the gate'sclassifyCitationagainst one board enumerated by the gate'senumerateBoard(195 pages, frontier #21629, 19,450 records, read from 20:17:47Z to 20:21:16Z), the same board for both readings.045b946257a520db9dThe citation count drops by 6, the 6 rewritten sites; no respelling stays a citation. The live counts did not move (src comment: 229 resolve as issues, 6 as pull requests, 1 cross-repo; test comment: 148, 11 and 1; test string: 100, 1 and 2; files outside
src: 10 resolve as issues). A third, raw reading (every#followed by 2 to 6 digits, whatever surrounds it,CHANGELOG.mdaside) counts 527 before and 521 after: also a drop of 6.Single reads over the issues endpoint (20:21:27Z): #6290 answers 404; the controls #6584, #6713, #6146, #6798, #6585, #6711, #16412 and #3447 answer 200. Comment
5865693155answers 200, and the control5973031304(the claim) answers 200. Noissuecommentordiscussion_rlink stands in the package (git grep exit 1).Per-number table
srccounts census sites,testthe test-comment sites.#6290e9b526597packages/lintsweepaa23e2c8f), re-proven herecurrent_user, ADR-0068 D1's canonical user root, joinsSCOPE_ROOTS, and the field-level*Whenrejection stops falling out of that list's omission and becomes@objectstack/lint's own rule with its own prescription. It wrote the'current_user'entry with its comment, the block comment after the list, and bothvalidate.test.tsdocblocks (the squash of PR #6584)The proof, per the earlier stages' standard:
git rev-parse --disambiguate=e9b526597matches exactly one commit,e9b5265970029092a8b7124f8c2df98aa6ac0b15.git merge-base --is-ancestorputs it under the base045b94625and underorigin/main045b94625(exit 0 both; exit 0 is self-proving, and the repository is not shallow).git blameat the base puts 5 of the 6 changed lines on it. The sixth,cel-engine.ts:120, was written byd5e9f6e7c, a later change that cites finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290 as the change in whichcurrent_userarrived. That arrival ise9b526597's diff: the'current_user',entry added toSCOPE_ROOTS.docs/adr, exit 1). ADR-0068 D1 records thatcurrent_useris the canonical spelling, a decision these sentences already cite by its own ADR id. It does not record the list change that the number stood for.Wordings to check
All 6 rewrites swap a tag in place, in stage 1's forms:
(#N)became(commit SHA)atcel-engine.ts:147and:160.[#N]became[commit SHA]atvalidate.test.ts:933and:958.current_userarrived in finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290」 became 「current_userarrived in commit e9b5265」 (cel-engine.ts:120). This is thepackages/lintsweep's wording for the twin sentence invalidate-expressions.ts.cel-engine.ts:173).A cross-package quotation stays verbatim.
packages/lint/src/validate-expressions.ts:769quotes this package'sSCOPE_ROOTSentry as "the last one this list was missing". That phrase is all oncel-engine.ts:146, which this stage does not touch, and the rewritten(commit e9b526597)is on the next line. No other file cites a line ofcel-engine.ts(git grep forcel-engine.ts:followed by a line number, no hit).No reflow. No line was reflowed, so
:120,:173andvalidate.test.ts:958are now longer than their block's wrap.eslint.config.mjsdeclares no line-length rule, and a reflow would move neighbouring lines.Sites left
ittitles atvalidate.test.ts:950and:964(finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290). Strings are outside this stage's surface, and the number's anchor is in this stage's table. Non-test strings cite no dead number.src: the release-ownedCHANGELOG.mdnames finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290 on 4 lines (2875, 2946, 4350, 4421); left.packages/lint/src/validate-expressions.test.tscites finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290 on 9 lines. That is another lane's test file, and thepackages/lintsweep's own PR notes it.Mechanical guard: no code token moves
The guard compares base
045b94625against the tree over both touched files, with TypeScript 6.0.3, to the earlier stages' two-reading specification. Stage 11's guard script was a scratch file, so it was rewritten here to that specification and proven with the controls below.forEachChildwalk. Comments are trivia there, and JSDoc is never visited. A leaf that is not itself a token is re-scanned with trivia skipped.getChildrenwalk, with JSDoc nodes skipped. String, template and numeric literals are compared in full on both readings.Results, at
7a520db9d(the later commit touches neither file):cel-engine.ts) and 「Pinned as behaviour」 to 「Pinned as BEHAVIOUR」 (validate.test.ts). 0 files changed (exit 0 each).export const SCOPE_ROOTStoXSCOPE_ROOTS,cel-engine.ts): DIFFER on both readings (exit 1).toContain('current_user')to'current_useR',validate.test.ts): DIFFER on both readings (exit 1).${root}.xtemplate to${root}.y,validate.test.ts): DIFFER on both readings (exit 1).maxAstNodes: 256to257,cel-engine.ts): DIFFER on both readings (exit 1).Each mutation went through
scripts/ablation-replace.mjs(wrap mode; the anchor hit 1 before and 0 after, and the blob changed). It ran under a shell trap that restores by absolute path fromHEAD. Each restore was proven equal to itsHEADblob (6fa1738be716,832c4f069ec7), and afterwardsgit diff HEADwas empty and the tree clean.Changeset:
patch(distmeasured)files[]isdist,README.mdandCHANGELOG.md, and the package is not private. One script ran under the shared verify lock (VERDICT command-exit 0, held 75s, shared-box seconds), at7a520db9d. It built the dependency closure first (pnpm --filter '@objectstack/formula^...' build, exit 0), then ran the package's ownbuild(tsup andcheck-dts-emitted) three times, exit 0 each:Leg 1, the head text: 6
distfiles hashed (index.js,index.mjs, their sourcemaps,index.d.ts,index.d.mts). 2 of the 4 rewritten non-test lines appear verbatim indist:SCOPE_ROOTSdocblock line (cel-engine.ts:120) inindex.d.tsandindex.d.mts;// (commit e9b526597).line inside the list (cel-engine.ts:147) inindex.jsandindex.mjs.The other 2 (
:160,:173) sit in a block comment the build drops.Leg 2, the base text put back in
cel-engine.ts(proven equal to its base blob667f87448eeb, written to the tree only): 4 of the 6 files differ from leg 1 (index.d.ts,index.d.mts,index.js,index.mjs); the two sourcemaps do not.scripts/ablation-dist-preflight.mjsfinds the base marker 「arrived in finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290) is silently unreported at」 in the 2 declaration files (exit 0).Leg 3, after the proven restore (equal to its
HEADblob6fa1738be716,git diff HEADempty, porcelain empty): all 6 files are byte-identical to leg 1. The preflight's--absentreading exits 0 with a clean tree. So the build is deterministic, and the difference is the rewrite.So the rewrite ships.
.changeset/20595-formula-provenance-anchors.mddeclares apatchfor@objectstack/formula, comment text only, with the claim'sClause-②: noline. The anchor is a commit, so the changeset names no ADR, repository qualifier or bracketed substitution. It says which published files carry the reworded text, as measured above. The changeset commit touches no file underpackages/formula.Gates (head
b89eb86cb)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsatb89eb86cb(3 paths against merge base045b94625) derived 63 commands. All 63 ran (20:36:36Z to 20:47:06Z, after the workspace build), each exit code captured before any pipe: 63 exit 0.--ranreports 「63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived zero) and exits 0..changeset/path adds: the ADR-0087 registration and empty-changeset pairs,check:objectui-changeset,check:pm-changeset-deadline-censusand two release self-tests;check:type-check-coverageandcheck:type-check-debt;check:engine-double-contract,check:objectql-double-limit,check:query-options-erasureandcheck:where-matcher.node scripts/check-issue-citations.mjsexits 0 (「no issue citations added against 045b946」).pnpm check:issue-citationsexits 0 (its self-test, 173 cases, 9 batteries).pnpm check:doc-authoringexits 0 (the sibling-package prose-id baseline holds, no growth).pnpm check:nul-bytesexits 0 (9,998 files, no raw control bytes), and a control-byte grep over the 3 changed files finds none (exit 1).check-adr-0087-registration(「1 non-breaking changeset(s) seen」),check-empty-changeset(「1 declaring changeset(s) added」),check-changeset-no-major(「nomajorbump」), andcheck:changeset-gate-self-tests. The Clause-② level axis ofcheck-changeset-no-majorreads the pull request body, so it does not apply to a local run; that reading is CI's.b89eb86cb:turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2): VERDICT command-exit 0, held 213s, shared-box seconds; 71 of 71 tasks, 17 cached.pnpm --filter @objectstack/formula test: 43 test files pass, 1,257 tests pass.pnpm --filter @objectstack/formula typecheck(tsc --noEmitandcheck:test-typecheckovertsconfig.test.json) exits 0.tsc --listFilesOnlyputs both touched files in a program:cel-engine.tsin both configs, andvalidate.test.tsintsconfig.test.json, whose program holds all 43 tracked test files (tsconfig.jsonexcludes them).b89eb86cb:--format json) over the 2 touched files plusdist/index.jsas the control.--print-configresolves a config for each.eslint.config.mjsnever enables type-aware linting (its lines 327 and 328 say so;--print-configshows noparserOptions.projectfor either file), so a comment edit cannot move the verdict on an untouched file.pnpm lintis CI's run.Acceptance notes
origin/mainat045b94625, and the worktree was cut there. Every reading above is on this branch's own tree. Just before this PR was opened,mainmoved one commit,a4f0cb0a4: aservice-automationchange, with its changeset and one lockfile line.packages/formula,check-issue-citations.mjsnordispatch-gates.mjs.git merge-treeof the head with it is clean (exit 0), and the anchore9b526597is under it too (--is-ancestor, exit 0).045b94625, and CI and the merge queue run on the merged ref.git rev-parse --is-shallow-repositoryanswers false), so no deepening was needed before the blame, ancestry and history readings.formula's 2 test-string sites (above);CHANGELOG.md(release-owned);packages/lint's test file (another lane).Generated by Claude Code