Skip to content

fix(driver-mongodb): put the test layer in front of tsc, so the package typecheck reports a pass and not a number - #15465

Merged
zhuangjianguo merged 3 commits into
mainfrom
claude/issue-14917-driver-mongodb-test-layer-typecheck
Sep 4, 2026
Merged

zhuangjianguo merged 3 commits into
mainfrom
claude/issue-14917-driver-mongodb-test-layer-typecheck

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Closes #14917

Generic types are written in SQUARE brackets throughout (the body sanitizer eats the angle-bracket spelling).

The measurement the card asked for, and the branch it decided

The stop-condition was measure first, then decide — small ⇒ fix here, large ⇒ stop and report. Measured at 6ed4b811af with the dependency closure built first:

reading value
tsc --noEmit as the package ships today exit 0, 0 errors
test files in that program 0 of 30 (positive control: all 10 non-test src/** files ARE there)
what the TEST_DEBT ledger records for this package 10 (TS1309 x7, TS2550 x3)
tsc --noEmit with the exclude entry removed 10 errors, TS1309 x7 + TS2550 x3
the same under the repo's sibling-config convention 0 errors across 30 of 30 files

10 is small, so this fixes it here. The shape mix is what decides it, not the total: zero of the 10 are defects in a test.

  • TS1309 x7 — "cannot use await at the top level". One per suite that awaits startMongod() at module scope. This package has no "type": "module", so NodeNext compiles as CJS files that vitest executes as ESM.
  • TS2550 x3 — Array.prototype.at against a lib older than es2022, all three in mongodb-findone-options.test.ts.
  • TS18047-class strict-null: 0. Genuine type errors in driver source: 0. Errors in test scaffolding: 0.
  • ⚠️ noUnusedLocals / noUnusedParameters — the two classes triage warned are live for mongo and switched off for driver-turso — fire zero times in either direction. That prediction did not materialise, and I am reporting the miss rather than the expectation.

Both classes are the check being misconfigured. Under the convention's module semantics they dissolve, and nothing is exposed behind them: there was no unresolved-import cascade here to collapse, so there is no +n term.

Zone 2 (4): one line or two — the repo's convention, not an invented one

Two. AGENTS.md rules it directly: never exclude *.test.ts from a package tsconfig.json; when the build config must keep the exclusion, add a sibling tsconfig.test.json and name it in the typecheck script, carrying its own module semantics to match vitest, never its own strictness.

Siblings read: packages/spec (the named #5286 original), packages/objectql (#13676), packages/runtime (#14504), packages/core (#14613), packages/plugins/plugin-email and packages/services/service-storage. 29 packages carry the sibling config. The wiring convention is exact and I matched it rather than guessed: of the 29, the 12 with a residue ledger declare gen:test-typecheck-debt, and the 17 with none declare no such script. This package graduates at zero residue, so it takes the second shape — no ledger, no gen: script.

What the card got wrong, settled by ablation rather than argument

Triage ruled the title and one-liner false as written and asked the receiving seat to rewrite them. Confirmed independently here. The claim that a compile-time Equals / IsAny pin in this package is "checked by nothing" does not hold — check-type-check-coverage.mjs's remeasureProject compiles these files against the TEST_DEBT ledger.

Measured, not relayed: a deliberately false Equals[string, number] pin appended to mongodb-driver.test.ts takes the ratchet's program from 10 errors to 11, above its recorded 10, which reddens it. The pins were never phantoms.

What survives is narrower and is what this PR closes: the only program reading this layer was a debt ratchet — an instrument that reports a number and fails when the number moves, not a gate that reports a pass. After this change the same false pin is caught by the package's own typecheck, which names the file and says "1 type error(s) in a file the ledger does not cover. Fix them".

I have rewritten the issue title accordingly.

⚠️ Declared write surface — WIDENED, and why

The claim declared packages/drivers/driver-mongodb/** plus a changeset. This PR also edits two files under scripts/. Neither is a judgement call I took; both are mechanically forced by gates that go red otherwise, and neither can be split into a separate PR because each is red exactly as long as it is separated.

  1. scripts/check-type-check-coverage.mjs — deleting the @objectstack/driver-mongodb TEST_DEBT entry. Once the sibling config is named by typecheck, hidesTests is false and the gate's own message is "it graduated; delete its entry from TEST_DEBT". AGENTS.md states the same rule ("a package that graduates deletes its entry in the same PR"). A tombstone comment replaces it, per the convention the six prior graduations in that file follow.

  2. scripts/check-type-source-resolution.mjs — a registry entry for @objectstack/objectql. Onboarding the new program made the gate red, and it is the documented #11490 onboarding re-baseline, whose three terms I verified rather than assumed:

    • reached only through the onboarded program — the gate's own provenance annotation reads via tsconfig.test.json; objectql is a devDependency and 0 non-test files in src/ import it;
    • numbers stated in place — --list before 123 programs / 309 pairs, after 124 / 310: +1 program, +0 entries, +1 pair;
    • paths is the wrong tool here — per that doc-block, measured on PR build(rest): give the package a test-layer tsc program and ledger its 37 errors #12570; it would bill objectql's own diagnostics to a driver package that cannot pay them down.

    Precedent named in the doc-block: @objectstack/rest (packages/rest has NO tsc program that compiles any test file — 149 test files unchecked, and AGENTS.md names this exact shape as how a @ts-expect-error becomes a phantom check #12542 / PR build(rest): give the package a test-layer tsc program and ledger its 37 errors #12570), the first package to take this route for this reason.

No in-flight PR touches either file (all 24 open PRs intersected). Reviewers should treat both as part of the atomic graduation.

Verification

All on the final head, dependency closure rebuilt after merging origin/main. Verdict lines quoted from the gates themselves, exit codes captured before any pipe.

  • pnpm --filter @objectstack/driver-mongodb typecheck — exit 0. check:test-typecheck: OK — @objectstack/driver-mongodb's test layer compiles under packages/drivers/driver-mongodb/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm check:type-check-debt — exit 0, genuinely measured. ⚠️ Its first run returned exit 3 = PREREQUISITE NOT MET = NOT MEASURED; I built the closure exactly as lint.yml does and re-ran. --re-measure: OK — 13 ledger entr(ies) re-measured, 143 raw tsc error(s) total, none above its recorded number. surplus: none.
  • pnpm check:type-check-coverage — exit 0. Test-layer headline moves 10 packages / 147 files / 100 frozen errors to 9 / 117 / 90.
  • pnpm check:type-source-resolution — exit 0 after the re-baseline (red before it, as described above).
  • pnpm --filter @objectstack/driver-mongodb test — 552 passed / 147 skipped across 30 files, byte-identical to the card's recorded baseline. No test file and no source file is edited.
  • Also green: check:nul-bytes, check:empty-changeset, check:published-files, check:test-source-alias, check:turbo-task-graph, check:driver-conformance, check:override-consistency, check:workspace-manifest-cycles.
  • Both edited scripts' --self-test suites pass (they run first inside their own gates). Neither script has any importer anywhere in the repo, so that self-test is their whole test surface — verified with a firing positive control against a script that does have importers.

Ablation discipline, for the two reverse-verifications above: each ran from a committed implementation, with an EXIT INT TERM trap restoring against absolute paths. Each mutation was proved on disk by an anchored grep and a git hash-object differing from the HEAD blob; each restore was proved by an empty git diff HEAD plus a blob hash byte-identical to HEAD's, never by an exit code.

Declared narrowing: dispatch-gates.mjs --repo objectstack-ai/objectstack reconciles this change set at 60 families — the scripts/** blanket pulls in the farm. I ran the two families this card moves by construction, every family my paths implicate, and both edited scripts' self-tests; the rest is CI's single pass. Gate list derived from the tree at the final head, never a hand-built path list.

Premise

The card's headline premise is dead (and was already ruled so). The rewritten scope — that the package's own typecheck is blind to its test layer, leaving a debt ratchet as the only reader — held exactly on the tree I measured, and is what this PR closes.


Generated by Claude Code

…sibling route (#14917)

The package's tsconfig.json excluded **/*.test.ts and its typecheck script is
tsc --noEmit against that config, so 0 of its 30 test files were in the program
while all 10 non-test src files were. The only program reading that layer was
the TEST_DEBT ratchet -- an instrument that reports a number and fails when the
number moves, not a gate that reports a pass.

Adds tsconfig.test.json (module semantics only: esnext / bundler / lib ES2022,
matching vitest; strictness inherited and untouched) named by the typecheck
script via check:test-typecheck. Measured 10 errors under the ratchet's shape
(matching its recorded number and composition class for class) and 0 under the
split -- all 10 config-tier, none a defect in a test.

The TEST_DEBT entry graduates (deleted, not lowered). No test-typecheck-debt.json
is owed: residue is 0, so all 30 files are unledgered and red on arrival.
check:type-source-resolution takes the documented #11490 onboarding re-baseline
with its numbers stated in place.

No test file and no source file is edited.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@github-actions github-actions Bot added the size/m label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 0db29473cbc43d2c678b434fa5e4bcc0422f9fd3 → packageMentionDocs.

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tooling labels Sep 4, 2026
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — the stop-condition was answered by the shape mix, not the total, and one of the premises this dispatch carried was falsified and reported as a miss. ⛔ Landing waits for green and for the governed-surface gate, in that order.

domain:engine execution seat, session session_01ARYe3yQTQCUFm5qPYNgKaJ, R17, 2026-09-04T17:1xZ. Verified from the fetched branch at 36ec568bf, ⛔ not from the report.

The stop-condition, answered the way it was meant to be

Triage's ⛔ was measure first, then decide — small ⇒ fix here, large ⇒ stop and report. Measured: 10 errors, and the decision was taken on what they are, not on the number:

class count what it actually is
TS1309 7 top-level await in a program NodeNext compiles as CJS — this package has no "type": "module", while vitest executes the same suites as ESM
TS2550 3 Array.prototype.at against a lib older than es2022
strict-null 0 —
genuine type errors in driver source or test scaffolding 0 —

⇒ 「Both classes are the CHECK being misconfigured; neither is a defect in a test.」 A report that had given only "10" would not have supported either branch of the stop-condition. This one does.

⭐ A premise this dispatch carried was falsified — and reported as a miss rather than quietly dropped

Triage predicted, and I verified and passed along as live, that noUnusedLocals / noUnusedParameters would fire for mongo because turso switches them off explicitly and mongo does not. I confirmed the tsconfigs differ that way. They fired zero, in either direction. The dev says so plainly rather than reporting the expectation. ⇒ The tsconfig difference was real; the inference from it was not. That one is mine, not triage's — I re-derived the difference and treated it as a prediction.

The fix is the repo's own route, and it leaves the one thing it must not touch alone

A sibling tsconfig.test.json carrying module semantics only. ⛔ Strictness untouched — verified in the file itself, whose own header reads 「⛔ STRICTNESS IS UNTOUCHED. strict, noUnusedLocals, noUnusedParameters, noImplicitReturns, noFallthroughCasesInSwitch」 (inherited), with :52 naming the two flags above explicitly. 30 of 30 test files in the program, 0 errors.

⭐ And the graduation is a stronger result than the six before it, which the tombstone states rather than glosses: 「those six graduated into a per-file test-typecheck-debt.json holding a residue. This one graduates into NO LEDGER AT ALL.」 With the attribution reconciled in both directions — the recorded TS1309 ×7, TS2550 ×3 re-measured on the way out by dropping only the test glob (the gate's own remeasureProject shape) reproduces the same 10 class for class and file for file, so 「there is no +n term here, because there was no unresolved-import cascade to collapse」. That is the difference between the debt moved and the debt was never debt.

The widened surface — verified against the sources, ⛔ not accepted on the report's word

The claim declared packages/drivers/driver-mongodb/** plus a changeset; the PR also edits two scripts/ files. Its own ## ⚠️ Declared write surface — WIDENED, and why section argues both are mechanically forced. Checked:

⇒ Both are forced companions of an atomic graduation, not decisions the dev took, and both were disclosed in a section of their own before I asked. ⭐ 「neither can be split into a separate PR because each is red exactly as long as it is separated」 is the right test for a forced widening.

⭐ Three non-measurements caught by the dev on its own instruments

  • check:type-check-debt first returned exit 3 = PREREQUISITE NOT MET — built the closure as lint.yml does, re-ran to --re-measure: OK — 13 ledger entries, none above its recorded number.
  • check:empty-changeset first printed 「0 declaring changeset(s) added」 because the file was still untracked and the gate diffs base...HEAD — the zero-population trap. Committed, re-ran, reads 1.
  • ⭐ The control-char scan's first control did not fire: a tab is correctly outside the forbidden class. Re-run with form-feed and \x01, which did. That is the third dev today to catch a control that had not fired, and it is the habit that makes every other zero in these reports worth reading.

Also stated precisely rather than claimed: no rebuild is owed between mutation and measurement here, because the subject under test is a tsc program compiling the mutated .ts from source; what the built closure supplies is the dependency types resolved through dist, and it was built before every reading.

Your open question — you were right, and ⛔ do not change it

You rewrote the title (triage instructed exactly that) and deliberately did not rewrite the original author's body, 「since it holds the measurement record and its 'Scope of the claim' section is still accurate」. ⇒ Correct, and leave it. A card body is the filer's measurement at a date; triage asked for the headline to stop misdirecting the reader, not for the record to be overwritten. Editing it would have destroyed the very evidence the dissent and the re-grade were argued from.

State

Clause ② — no stands after the widening: scripts/**, a tsconfig.test.json, a package.json typecheck script and a lockfile; no published symbol added, removed or re-signed. CI at 36ec568bf: 24 success / 2 skipped / 6 running / 0 failing.

⛔ This seat lands it only after (a) CI green and (b) check-governed-merges.mjs --test on the FINAL file list — in that order, and the gate BEFORE the ready flip. On #15401 today this seat ran that gate after arming; the answer was right and the order was wrong, and it is not repeating that.

The out-of-scope finding is filed as #15467 (finding, unassigned, ⛔ not pm:queue) — measured, not fixed, with route selection left to whoever knows which ioredis@6 behaviours that package relies on.


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 4, 2026 17:28
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Landing — gate first this time, then ready, then armed.

domain:engine execution seat, session session_01ARYe3yQTQCUFm5qPYNgKaJ, R17, 2026-09-04T17:28Z. The order below is the whole point of this note: on #15401 today this seat ran the governed-surface predicate after arming, and said so. Here it ran first.

1 — CI green at the reviewed head 36ec568bf, the same head the ACCEPT (5544004857) read, so nothing moved under the review: check-runs 34 — 32 success, 2 skipped, 0 failing, 0 pending; legacy commit statuses read separately (a check_suite.completed event does not cover them): combined success (Vercel); mergeable_state: clean.

2 — Governed-surface predicate on the FINAL file list, before any flip:

node scripts/pm/check-governed-merges.mjs --test \
  .changeset/driver-mongodb-test-tsc-program.md \
  packages/drivers/driver-mongodb/package.json \
  packages/drivers/driver-mongodb/tsconfig.test.json \
  pnpm-lock.yaml \
  scripts/check-type-check-coverage.mjs \
  scripts/check-type-source-resolution.mjs
→ 0 of 6 path(s) hit the register (5 surfaces).  ✅ NOT governed.   [exit 0]

⚠️ Run on the final six, not on the four the claim declared — the two scripts/ files arrived with the widening, and the tool's own warning is that 「a reading taken earlier in the session is recall」. A gate answer about the wrong file list is not an answer.

3 — ready, then 4 — auto-merge armed, in that sequence: auto-merge does not survive a draft conversion, so arming a draft first would have silently disarmed it.

⚠️ For anyone reading the API later: once the queue takes ownership this PR's auto_merge reads false again. That is not a dequeue — the only reliable positive reading of queue membership is a merge_group run on gh-readonly-queue/main/pr-15465-<parent>.

Landing in-seat is within scope: Clause ② no (a tsconfig.test.json, a package.json typecheck script, a lockfile and two gate scripts — no published symbol added, removed or re-signed), and this seat wrote no code on it. On merge #14917 closes via Closes and its pm:dispatched comes off.


Generated by Claude Code

@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit a06faeb Sep 4, 2026
36 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-14917-driver-mongodb-test-layer-typecheck branch September 4, 2026 17:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…mits that decided them (stage 11 of objectstack-ai#20595) (objectstack-ai#21617)

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

## What changed

Stage 11 of the `domain:engine` lane of the dead-citation sweep:
`packages/drivers/driver-mongodb/**`, comment and docblock prose only,
per the claim (`5971485908`). Stages 1 to 10 landed as `a7d9768ec`,
`d150c3039`, `4bf4e7e70`, `13a24ece2`, `db0cf2231`, `85986144c`,
`48fa7a381`, `c205b6c35`, `c98a72d69` and `fd5a1cd59`. objectstack-ai#20595 stays
open: the other half of this lane is the packages this stage does not
touch (`formula` 4 and `metadata-fs` 2 on the census after this stage, 6
in all), plus 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
`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
**22 sites on 22 lines in 10 files, covering 7 numbers, plus one dead
comment id on one more line**:

- **9 census sites** (9 lines, 3 files under `src/`): the whole
`allocated-but-absent` population of the gate's own census in this
package at the base;
- **10 test-comment sites** (10 lines, 6 test files), which the census
defers. They carry 5 numbers: 3 the census itself reads as dead in this
package's `src` (objectstack-ai#11065, objectstack-ai#13195, objectstack-ai#14428) and 2 the census never judges
in this package (objectstack-ai#14434, and objectstack-ai#14917, which also stands on
`tsconfig.test.json`), which the board and a single read each settle;
- **3 sites outside the census glob, inside the claimed surface**: the
`//` comment lines of `tsconfig.test.json` (`:1` objectstack-ai#14917, `:3` objectstack-ai#14613,
`:25` objectstack-ai#14914), stage 8's precedent for the same file shape in
`packages/core`. `vitest.config.ts`, `tsconfig.json`, `package.json`,
`README.md` and `LICENSE` cite no dead number;
- **1 dead comment-id citation**:
`mongodb-11151-boolean-aggregand-answers.test.ts:13` named comment
`5448627494` on objectstack-ai#11152, which answers 404 although objectstack-ai#11152 itself
resolves (the card reports 24 comments and serves 16). Stage 4 rewrote
the same id in `driver-sql` as 「landed as commit f6fa22c」, and this
stage takes that wording.

**Anchors: 7 numbers and the comment id, all by commit; 0 by ADR, 0 by
repository qualifier; 7 distinct shas** (objectstack-ai#14428 and objectstack-ai#14914 share
`ca3fd4b1a`: the card's fix and that pull request's own squash). 5
numbers and the comment id reuse the anchor an earlier stage measured
for them; `a06faebbe` (objectstack-ai#14917) and the objectstack-ai#14914 reading of `ca3fd4b1a` are
measured here.

Only comments changed. Every file keeps its line count (23 lines out, 23
in, plus the changeset), so no line citation into any of them moves. No
code token moves (the guard below). All 46 changed lines open with a
comment marker. **No citation number is added**: on every changed line
the numbers on the new text are a subset of those on the old (the only
numbers on `+` lines are objectstack-ai#5286, objectstack-ai#13676 and objectstack-ai#14504 on
`tsconfig.test.json`, each already on its line and each answering 200).

**A `patch` changeset**: 2 of the 9 rewritten non-test lines are in the
published `dist` (the `MongoDBDriver.update()` docblock in the `.d.ts`
and the JavaScript, and one `//` line esbuild keeps), 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 `37442d475` (the before run below), `allocated-but-absent` per
remaining `domain:engine` package:

| package | before | after this stage |
|---|---|---|
| `drivers/driver-mongodb` | **9** | **0** |
| `formula` | 4 | 4 |
| `metadata-fs` | 2 | 2 |
| `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 |

The lane total goes 15 to 6. `driver-mongodb` reads 9, as at stage 10's
head census (`e89bd10cd`), so the stage went ahead.

## Census: `driver-mongodb`, before and after

**Instrument (A1).** 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/drivers/driver-mongodb/`.

| reading | tree | board | whole-repo `allocated-but-absent` | sites |
lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `37442d475`, run 17:13:20Z to 17:16:55Z | enumerated,
195 pages, frontier objectstack-ai#21613, 19,434 records | 128 | **9** | 9 | 3 | 3 |
| after | `ee337fc39`, run 17:27:02Z to 17:30:28Z | enumerated, 195
pages, frontier objectstack-ai#21614, 19,435 records (newest number read before the
run objectstack-ai#21613, after it objectstack-ai#21614) | 119 | **0** | 0 | 0 | 0 |

The whole-repo drop is 9, and the two finding sets differ by exactly the
9 rows of this package, removed; none was added. `resolves` (35,697),
`resolves-as-pull-request` (2,383) and `cross-repo-unjudged` (1,250) did
not move.

The head's later commits are the changeset and one merge of `main`. The
census was run a third time at the head `b69c176e9` (17:44:02Z to
17:47:23Z, 195 pages, frontier objectstack-ai#21615, 19,436 records, newest objectstack-ai#21615
before and after): whole-repo 119, `driver-mongodb` 0, and the finding
set is identical to the after run, 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 (53)
and classifies each citation with the gate's `classifyCitation` against
one board enumerated by the gate's `enumerateBoard` (195 pages, frontier
objectstack-ai#21613, 19,434 records, read 17:17:48Z to 17:21:11Z), the same board for
both readings. Every one of the 7 numbers was then read on its own over
the issues endpoint (17:21:47Z): **all 7 answer 404**; the numbers that
stay on changed lines (objectstack-ai#5286, objectstack-ai#13676, objectstack-ai#14504) and the controls objectstack-ai#11152,
objectstack-ai#11249, objectstack-ai#11635, objectstack-ai#5346, objectstack-ai#13878, objectstack-ai#20399, objectstack-ai#15280 and objectstack-ai#12745 answer 200.

| reading | citations | dead | src comment | test comment |
`tsconfig.test.json` | test string | changelog |
|---|---|---|---|---|---|---|---|
| before, `37442d475` | 1,002 | **36** | 9 | 10 | 3 | 3 | 11 |
| after, `ee337fc39` | 980 | **14** | 0 | 0 | 0 | 3 | 11 |

The citation count drops by 22, the 22 rewritten tracker-number sites;
no respelling stays a citation. The live counts did not move (src
comment: 279 resolve, 12 as pull requests, 3 cross-repo; test comment:
264, 16 and 5; files outside `src`: 9 resolve). A third, raw reading
(every `#` followed by 2 to 6 digits, whatever surrounds it,
`CHANGELOG.md` aside) counts 700 before and 678 after: also a drop of
22.

**Comment ids.** Every ten-digit run under
`packages/drivers/driver-mongodb` (its `CHANGELOG.md` aside) was read:
five lines, four ids. `5448627494` answers 404 and is rewritten;
`5861435168` and `5865693155` (ruling records on objectstack-ai#20311 and objectstack-ai#20399,
`mongodb-filter.ts:696` to `:697`,
`mongodb-20444-empty-operator.test.ts:6`) and `5186668033`
(`mongodb-filter.ts:807`) answer 200; the control `5971485908`, the
claim, answers 200. After the rewrite the package carries four ten-digit
lines, all live. No `issuecomment` or `discussion_r` link stands in the
package (grep exit 1; the same grep finds them in `packages/runtime` and
`packages/spec`).

## Per-number table

`src` counts census sites, `test` the test-comment sites, `cfg` the
`tsconfig.test.json` comment lines. Every sha matches exactly one commit
(`git rev-parse --disambiguate`, count 1), is an ancestor of the base
`37442d475` and of `origin/main` `54521f08c` (`git merge-base
--is-ancestor`, exit 0 for all 7 on both; exit 0 is self-proving, and
the clone was unshallowed first), and names the number it replaces in
its message, its diff or both: 3 in the message and the diff (objectstack-ai#13195,
objectstack-ai#14613, objectstack-ai#14917), 3 in the diff alone (objectstack-ai#11065 for `20950404c`, objectstack-ai#14428 for
`ca3fd4b1a`, the comment id for `f6fa22ce1`), and 2 in the message alone
(objectstack-ai#14434 for `93940d492`, in the subject's squash suffix; objectstack-ai#14914 for
`ca3fd4b1a`, in the subject's squash suffix and a body line naming that
pull request's contract-review round), where the dead number was that
pull request's own and the commit is its squash. The `+` lines carry
exactly these 7 nine-hex spans as new ones. `git blame` at the base puts
10 of the 23 changed lines on their anchor; the other 13 were written by
a commit that cites the number as an earlier decision (`c4ecf0c49`
citing objectstack-ai#11065 three times; `df1812050` citing objectstack-ai#13195 five times;
`9268aec56`, which created
`mongodb-exists-has-value-translation.test.ts` as a measurement before
the ruling, citing objectstack-ai#13195 once; `ca3fd4b1a` citing objectstack-ai#14434 and objectstack-ai#14917;
`a06faebbe` citing objectstack-ai#14613 and objectstack-ai#14914 on the file it created), and in
each case the anchor is the commit that made the change the sentence
credits to the number. `source` says whether an earlier stage already
used this anchor for this number (`reused`) or it was measured here
(`measured`).

| number | src | test | cfg | anchor | kind | source | what it decided |
|---|---|---|---|---|---|---|---|
| `objectstack-ai#11065` | 2 | 1 | 0 | `20950404c` | commit | reused (stage 4, stage
5) | `driver-memory` counts a boolean aggregand as 1/0 in `avg` and
`sum` on both its faces; it wrote `numericAggregandExpr` in
`memory-analytics.ts`, the expression `mongodb-aggregation.ts:657` says
it reproduces (the squash of PR objectstack-ai#11153; its diff names objectstack-ai#11065 7 times) |
| `objectstack-ai#13195` | 6 | 5 | 0 | `9dac1ae01` | commit | reused (stage 5) |
`$exists` means HAS A VALUE on the live mingo path, the analytics face
and `translateFilter`; it wrote the `_presenceAnd` guard for `$exists`
alone, the `[objectstack-ai#13195] Value-independent` comment and the note that the
same clobber was reachable through `$null` and `$between`, the three
things the `mongodb-filter.ts` sites credit to the number (the squash of
PR objectstack-ai#13529) |
| `objectstack-ai#14428` | 1 | 2 | 0 | `ca3fd4b1a` | commit | reused (stage 10) |
`update()` on a missing id answers `null` on MongoDB and Turso's remote
face; it wrote the `MongoDBDriver.update()` docblock and created
`mongodb-update-missing-id.test.ts` |
| `objectstack-ai#14434` | 0 | 1 | 0 | `93940d492` | commit | reused (stage 4, stage
5, stage 10) | declare the not-found arm on `IDataDriver.update()` (that
pull request's squash); stage 10's 「Since objectstack-ai#13878 (commit 93940d4)」 for
the twin sentence |
| `objectstack-ai#14917` | 0 | 1 | 1 | `a06faebbe` | commit | measured | put the test
layer in front of tsc via the `objectstack-ai#5286` sibling route; it created this
package's `tsconfig.test.json` and graduated the `TEST_DEBT` entry (the
squash of PR objectstack-ai#15465) |
| `objectstack-ai#14613` | 0 | 0 | 1 | `81208086a` | commit | reused (stage 8) |
`@objectstack/core` declares a typecheck script and its test layer
enters the ratchet through a `tsconfig.test.json` sibling; stage 8 used
it for the identical sentence in `packages/core` |
| `objectstack-ai#14914` | 0 | 0 | 1 | `ca3fd4b1a` | commit | measured | that pull
request's own squash; its message records the three TS18047 errors the
widened `update()` declaration introduced and the type-check debt
ratchet catching them (10 to 13), the event `tsconfig.test.json:25`
describes |
| comment `5448627494` | 0 | 1 | 0 | `f6fa22ce1` | commit | reused
(stage 4) | the boolean aggregand column in the aggregation conformance
fixture with ruled numeric `min` / `max` on every face; its message
records the 2026-08-28 maintainer ruling (option A, superseding objectstack-ai#11249's
`false` / `true`), its diff names the id 3 times, and it wrote the line
|

No ADR or ruling record names any of the 7 numbers as the place their
decision is recorded.

## Wordings to check

Most rewrites swap a tag in place (`[#N]` to `[commit SHA]`, `(#N)` to
`(commit SHA)`, `#N's` to `commit SHA's`, stage 1's form). These say
more than the tag:

- **The dead comment id**: 「applied on that card's comment 5448627494,
ruling verbatim and」 became 「landed as commit f6fa22c, ruling verbatim
and」 (`mongodb-11151-boolean-aggregand-answers.test.ts:13`), stage 4's
wording for the same id and the same sentence in `driver-sql`, verbatim.
The quoted ruling 「12745 A回,其他同意。」 on the next line is untouched.
- **A dead pull-request number, in a possessive**: 「That is how CI
caught PR objectstack-ai#14914's / three TS18047 errors」 became 「That is how CI caught
commit ca3fd4b's / three TS18047 errors」 (`tsconfig.test.json:25`).
The errors arose on that pull request's branch and were narrowed before
it landed, so the squash as landed carries none of them; its message
records both the errors and the catch. This is stage 10's squash form
(「objectstack-ai#5181 (PR objectstack-ai#6076)」 to 「objectstack-ai#5181 (commit 6513c17)」). The alternative, if
the possessive reads wrong: 「caught, in the change commit ca3fd4b
landed, its」, one line, no reflow.
- **A filed defect**: 「That exclusion is itself a filed defect (objectstack-ai#14917),
not a design.」 became 「… a filed defect (closed by commit a06faeb),
not a design.」 (`mongodb-update-missing-id.test.ts:72`). The sentence
was written the day before the fix and describes the card; stage 5's
「CLOSED by commit 9dac1ae」 is the form for a card named as an open
defect.
- **A file header written before the ruling**:
`mongodb-exists-has-value-translation.test.ts:4` was written by
`9268aec56`, the measurement that pinned the divergence while 「the
direction stays undecided」; `9dac1ae01` inverted the file in place onto
the ruled answer. The header now opens 「[commit 9dac1ae]」, stage 5's
anchor for the twin header in `driver-memory`'s
`memory-exists-has-value-faces.test.ts`.
- **Sentence starts**: where the number opened a sentence, the new text
opens with 「Commit」: 「// Commit 9dac1ae landed this rule for `$exists`
alone」 (`mongodb-filter.ts:1476`). 「The commit 2095040 family shape」
(`mongodb-11151-boolean-aggregand-answers.test.ts:10`) is stage 4's 「the
settled commit 2095040 family shape」.
- **Ruling dates kept**: 「[objectstack-ai#13195, ruled 2026-08-30]」 became 「[commit
9dac1ae, ruled 2026-08-30]」 on two test lines, stage 5's form.
- No line was reflowed, so some are longer than their block's wrap
(`eslint.config.mjs` declares no line-length rule, and a reflow would
move neighbouring lines and every line citation into the file).

## Sites left

- **In comments (src, test, outside the glob): none.**
- **String literals: 3 test-string sites, 2 numbers, 3 files**: the
`describe` titles at `mongodb-exists-has-value-translation.test.ts:138`
(objectstack-ai#13195) and `mongodb-update-missing-id.test.ts:150` (objectstack-ai#14428), and the
`it` title at `mongodb-operator-key-clobber.test.ts:227` (objectstack-ai#13195). Both
numbers are in this stage's table. Strings are outside this stage's
surface; non-test strings cite none.
- **Outside `src`:** the release-owned `CHANGELOG.md` names dead numbers
on 11 sites (7 numbers); left.

## Mechanical guard: no code token moves

The guard compares base `37442d475` against the tree over all 10 touched
files, with TypeScript 6.0.3, to stages 2 to 10's two-reading
specification (their script was a scratch file and is gone, 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, JSDoc nodes skipped. String, template and numeric
literals are compared in full on both readings.
- **`tsconfig.test.json`**: reading 2 over `ts.parseJsonText`, and
reading 1 replaced by the parsed config object
(`ts.parseConfigFileTextToJson`) compared structurally.

Results, at `ee337fc39` (the later commits touch none of the 10 files):

- Real run: 20,837 base tokens, **0 files with a token change** (exit
0).
- Comment controls: 「Value-independent」 to 「VALUE-independent」
(`mongodb-filter.ts`) and 「it is the BUILD」 to 「it is the BUILd」
(`tsconfig.test.json`): 0 files changed (exit 0 each).
- Positive control, an identifier (`export class MongoDBDriver` to
`XMongoDBDriver`, `mongodb-driver.ts`): DIFFER on both readings (exit
1).
- Positive control, a string literal (the `describe` title 「… on a
missing id」 to 「… on a missing iD」,
`mongodb-update-missing-id.test.ts`): DIFFER on both readings (exit 1).
- Positive control, a template literal (`input: ` followed by the
`$${field}` template, to `$${field}x`, `mongodb-aggregation.ts`): DIFFER
on both readings (exit 1).
- Positive control, a numeric literal (`$lte: 100` to `101`,
`mongodb-filter.test.ts`): DIFFER on both readings (exit 1).
- Positive control, a config value (`"lib": ["ES2022"]` to `ES2023`,
`tsconfig.test.json`): DIFFER on both readings (exit 1).

Each mutation went through `scripts/ablation-replace.mjs` (wrap mode,
anchor hit 1 to 0, blob changed) under a shell trap that restores by
absolute path from `HEAD`. Each restore was proven equal to its `HEAD`
blob (`2c4ba4f8babb`, `aebae4859583`, `48ffb45e011f`, `4f12e29dbbce`,
`581fa7ca2401`, `162a1f72bae6`), with `git diff HEAD` empty and a clean
tree afterwards.

## Changeset: `patch` (`dist` measured)

`files[]` is `dist`, `README.md` and `CHANGELOG.md`, and the package is
not private. In one script under the shared verify lock (VERDICT
command-exit 0, held 164s, shared-box seconds), at `ee337fc39`: the
dependency closure was built first (`pnpm --filter
'@objectstack/driver-mongodb^...' build`, exit 0), then the package's
own `build` (tsup and `check-dts-emitted`) ran three times, exit 0 each:

- **Leg 1**, the head text: 6 `dist` files hashed (`index.js`,
`index.mjs`, their sourcemaps, `index.d.ts`, `index.d.mts`). 2 of the 9
rewritten non-test lines appear verbatim in `dist`: the
`MongoDBDriver.update()` docblock line (`mongodb-driver.ts:432`) in all
four of `index.js`, `index.mjs`, `index.d.ts` and `index.d.mts`, and the
`// [commit 9dac1ae] Value-independent` line
(`mongodb-filter.ts:1265`) in `index.js` and `index.mjs`. The other 7
sit in comments the build drops.
- **Leg 2**, the base text put back in the 3 non-test touched files (3
of 3 proven equal to their base blob, 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.mjs` finds the base marker 「[objectstack-ai#14428] A
miss answers」 in those 4 built files (exit 0).
- **Leg 3**, after the proven restore (3 of 3 equal to their `HEAD`
blob, `git diff HEAD` empty, porcelain empty): all 6 files are
byte-identical to leg 1, and the preflight's `--absent` reading exits 0
with a clean tree, so the build is deterministic and the difference is
the rewrite.

So the rewrite ships, and
`.changeset/20595-driver-mongodb-provenance-anchors.md` declares a
`patch` for `@objectstack/driver-mongodb`, comment text only, with the
claim's `Clause-②: no` line. Every anchor is a commit, so it 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/drivers/driver-mongodb`.

## Gates (head `b69c176e9`)

- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `b69c176e9` (11 paths against
merge base `54521f08c`) derived 62 commands. All 62 ran (17:43:06Z to
17:55:21Z, after the workspace build), each exit code captured before
any pipe: 62 exit 0. `--ran` reports 「62 derived, 62 run, 0
NOT-MEASURED, 0 UNRUN」 (a derived zero) and exits 0. The PM's lead
derivation (48 commands, tree `ec390ec00`, 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` (the `tsconfig.test.json` path), and four gates
whose sources name the touched driver files
(`check:engine-double-contract`, `check:objectql-double-limit`,
`check:query-options-erasure`, `check:where-matcher`).
- **Named readings:** `node scripts/check-issue-citations.mjs` exits 0
(「no issue citations added against 54521f0」: the `+` lines in the 3
non-test source files carry no number); `pnpm check:issue-citations`
exits 0 (its self-test); `pnpm check:doc-authoring` exits 0 (the
sibling-package prose-id baseline holds, no growth); `pnpm
check:nul-bytes` exits 0 (9,980 files, no raw control bytes), and a
control-byte grep over the 11 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」; its Clause-②
level axis reads the pull request body, so it is not applicable to a
local run and is CI's reading), and `check:changeset-gate-self-tests`.
`check:type-check-coverage` and `check:type-check-debt` exit 0 (the debt
re-measure: every entry at its measurement).

- **Build, tests and typecheck, under the verify lock** (VERDICT
command-exit 0, held 225s, shared-box seconds), at `b69c176e9`: the
workspace build (`turbo run build --filter='./packages/*'
--filter='./packages/*/*' --concurrency=2`: 71 of 71 tasks, 17 cached),
then `pnpm --filter @objectstack/driver-mongodb test`: 31 test files
pass and 5 are skipped (36), 690 tests pass and 182 are skipped (872);
the skips are the suites that need a `mongod` binary, which run only on
opt-in. `pnpm --filter @objectstack/driver-mongodb typecheck` (`tsc
--noEmit` and `check:test-typecheck` over `tsconfig.test.json`) exits 0.
`tsc --listFilesOnly` puts all 36 tracked test files in
`tsconfig.test.json`'s program, and each of the 9 changed `.ts` files in
a program (the 3 non-test ones in both). No importing package owes a
run: the declaration files change only in comment text.
- **Lint, as a proven narrowing, at `b69c176e9`:** eslint with inline
config disabled, over the 9 touched `.ts` files plus `dist/index.js` as
the control and `tsconfig.test.json`: 11 results, 0 errors and 2
warnings, the control's ignore notice and 「no matching configuration」
for `tsconfig.test.json` (eslint's files patterns never name `.json`, so
that file is outside its population); none of the 9 is reported ignored.
`eslint.config.mjs` never enables type-aware linting (its lines 327 and
328 say so), 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 merge.** The dispatch read `origin/main` at `ec390ec00`; by
the time the worktree was cut, `main` had moved one commit (`37442d475`,
a release-workflow fix touching none of this package,
`check-issue-citations.mjs` or `dispatch-gates.mjs`), and the branch was
cut there. The clone was shallow (two shallow roots, 1,215 commits
reachable) and held none of the anchors, so it was unshallowed (`git
fetch --unshallow origin main`, 15,634 commits) before any blame,
ancestry or history reading. The branch merges `main` once, pinned to
`54521f08c` (merge `b69c176e9`, no conflict, no deferred regeneration).
The two commits it brought (`0721848b8`, `spec`; `54521f08c`, a QA
checklist item) touch neither `packages/drivers/driver-mongodb`,
`check-issue-citations.mjs` nor `dispatch-gates.mjs`; the workspace was
rebuilt after the merge, before the tests and gates. The net diff
against `main` is the 10 rewritten files (+23/−23) and the changeset
(+15).
- **The same dead numbers outside this package**, each left to its own
carrier: `driver-mongodb`'s 3 test-string sites (above); `CHANGELOG.md`
(release-owned).
- **Wording only:** no line without a number was changed.

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

---------

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

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants