Repository navigation
Commit 8b39b77
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X1Cna1X7zGXBer8k5CGbdU4)_
Part of #25521
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Ruling-ref: 6348891160
Cut 12 of the `sql-driver.ts` split (#24586 ruling A, recorded in
`6270097840`). The claim is `6427355170`. The cut opens with a fresh
seam table (below), taken on `origin/main` `83e9d7187` (cut 11's merge),
and takes the area that pays best without breaking a series limit. The
bodies of eight `SqlDriver` methods move unedited into the **existing**
seam module `packages/drivers/driver-sql/src/sql-driver-schema-sync.ts`,
through its existing host `SchemaSyncHost`. Together they are the
canonical-encoding convergence that schema sync runs at init:
- the SQLite canonical backfills: `backfillCanonicalDatetimes`,
`backfillCanonicalTimes` and `backfillCanonicalJsonEncoding`, with
`countNonTemporalTextRows` and `convergeJsonColumn`, which they call;
- of the MySQL widening of legacy zero-precision columns: the two
`information_schema` reads `legacyMysqlTimestampColumns` and
`legacyMysqlTimeColumns`, and `runWideningAlters`, which runs the ALTERs
under the #9354 lock-wait bound.
The change is move-only, and the card stays open for later cuts. There
is no `docs/adr/**` edit, no `packages/spec/**` edit, no anchor entry
change, no gate edit and no new gate. Branch
`claude/project-thread-c22v4e` (the platform assigned it to this card
thread, as the claim records). The move is one commit, `ff5b18102`, on
`83e9d7187`. Then `origin/main` was merged once before opening
(`274ac8541`), which brought no `driver-sql` file.
**Net line count (the seat's rule).** `sql-driver.ts` goes from 20,827
lines to 20,492: **-335**.
- **Out:** the eight bodies, 278 lines (60 + 55 + 37 + 37 + 9 + 20 + 20
+ 40). Also out are four module-level declarations that only these
bodies read, 139 lines with their docblocks plus 4 separator lines:
`SQLITE_JSON_BACKFILL_PAGE_SIZE` (6),
`DEFERRED_DDL_LOCK_WAIT_TIMEOUT_SECONDS` (52), `isMysqlLockWaitTimeout`
(27) and `deferredDdlLockWaitError` (54). Finally, the import line of
`recoverUnencodedJsonText`, whose last reader moved; two `{@link}`
mentions of it remain in docblocks.
- **In:** the eight stub bodies, 85 lines (each is 4 lines, plus 2 per
field read and 1 per method read, plus its closing brace), and 2 comment
lines on the existing schema-sync import.
- The module goes from 1,154 lines to 1,653.
**Serial with the file's in-flight PRs.** I read the full file list of
every open PR twice: at start, on cut 11's head (29 PRs), and again
after cut 11 merged, before the first edit (28 PRs). No open PR touches
`sql-driver.ts`, `sql-driver-schema-sync.ts`, `turso-driver.ts`,
`check-tenant-chokepoint.mjs`,
`check-durability-degradation-log-level.mjs` or the driver-sql anchor
entries. #26138 touches `driver-sql/src/builtin-column-collision.ts`
only. #26267 (the `vitest.config.ts` of driver-turso and
driver-sqlite-wasm) merged in between, and the merge commit carries it.
#25640 is the release PR (changelogs and `package.json` versions).
## Seam table, fresh, on `83e9d7187`
- **Size and churn.** `sql-driver.ts` is 20,827 lines. The 30-day column
counts `git blame` lines from commits on or after 2026-09-26T22:04:20Z,
30 days before the base commit. History reaches 2026-09-12.
`scripts/pm/git-history.mjs count` proves 72 commits on the file in 30
days and 30 in the last 7. In all, 3,527 lines (16.9%) are from the last
30 days.
- **Class shape.** `SqlDriver` spans lines 5594-20752 with 350 members:
243 instance methods, 13 accessors and 32 statics, all 32 private or
protected.
- **How it was measured.** The same TypeScript compiler AST script cuts
9-11 used, which strips casts, parentheses, `!` and `satisfies` before
it reads `this`. Sections are the class's own banner comments.
| Lines | Section | First … last member | Lines | 30-day | Methods
(public) | Inferred public | getBuilder sites | Statics
(private/protected) |
|:--|:--|:--|--:|--:|:--|--:|--:|:--|
| 1-5593 | Module preamble | - | 5,593 | 850 | - | - | - | - |
| 5594-7185 | Class head: fields, statics, constructor, capability
getters | `name` … `assertDialectEmits` | 1,592 | 290 | 8 (1) | 1 | 0 |
18 (18) |
| 7186-7617 | Lifecycle | `connect` … `disconnect` | 432 | 155 | 8 (3) |
0 | 0 | 6 (6) |
| 7618-10128 | CRUD — IDataDriver core | `find` … `delete` | 2,511 | 736
| 43 (7) | 0 | 12 | 0 (0) |
| 10129-10740 | Bulk & Batch Operations | `bulkCreate` … `count` | 612 |
119 | 9 (6) | 0 | 7 | 0 (0) |
| 10741-10837 | Raw Execution | `rawStatementFault` …
`noteRuntimeIndexDdl` | 97 | 28 | 3 (1) | 0 | 0 | 0 (0) |
| 10838-10930 | Transactions | `beginTransaction` …
`rollbackTransaction` | 93 | 46 | 7 (5) | 0 | 0 | 0 (0) |
| 10931-11462 | Aggregation | `aggregate` …
`unresolvableAggregateColumnRefusal` | 532 | 60 | 5 (1) | 0 | 1 | 0 (0)
|
| 11463-11750 | Distinct | `distinct` …
`unresolvableDistinctColumnRefusal` | 288 | 17 | 3 (1) | 0 | 1 | 0 (0) |
| 11751-11866 | Window Functions | `findWithWindowFunctions` …
`findWithWindowFunctions` | 116 | 4 | 1 (1) | 0 | 1 | 0 (0) |
| 11867-11965 | Query Plan Analysis | `explain` … `analyzeQuery` | 99 |
1 | 2 (2) | 0 | 1 | 0 (0) |
| 11966-12024 | Schema Sync (syncSchema / init) | `syncSchema` …
`reclaimSpace` | 59 | 34 | 3 (3) | 0 | 0 | 0 (0) |
| 12025-13766 | Schema Sync (syncSchema / init) › Data-lifecycle
rotation (ADR-0057 P2) | `rotationStateByTable` …
`migrateMysqlTimeColumns` | 1,742 | 72 | 26 (4) | 0 | 0 | 0 (0) |
| 13767-14023 | Schema Sync (syncSchema / init) › Deferred schema DDL
(#3917) | `setDeferredDdl` … `flushDeferredSchemaDdl` | 257 | 25 | 6 (4)
| 0 | 0 | 0 (0) |
| 14024-14838 | Schema Sync (syncSchema / init) › Managed-schema drift &
reconcile (#2186) | `dialectName` … `createNullSafeUniqueIndex` | 815 |
237 | 23 (3) | 0 | 0 | 0 (0) |
| 14839-14890 | Schema Introspection | `listPhysicalColumns` …
`introspectSchema` | 52 | 46 | 2 (2) | 0 | 0 | 0 (0) |
| 14891-15161 | Internal helpers | `getKnex` … `auditMissingTenant` |
271 | 73 | 8 (1) | 0 | 1 | 0 (0) |
| 15162-17602 | Internal helpers › Filter helpers |
`tableNameForBuilder` … `applyFalseConstant` | 2,441 | 438 | 44 (2) | 0
| 0 | 0 (0) |
| 17603-17778 | Internal helpers › Field mapping | `mapSortField` …
`mapAggregateFunc` | 176 | 2 | 6 (0) | 0 | 0 | 0 (0) |
| 17779-17826 | Internal helpers › Window function builder |
`buildWindowFunction` … `buildWindowFunction` | 48 | 26 | 1 (0) | 0 | 0
| 0 (0) |
| 17827-19773 | Internal helpers › Column creation helper |
`nowColumnDefault` … `applyDeclaredColumnDefault` | 1,947 | 92 | 19 (0)
| 0 | 0 | 8 (8) |
| 19774-20071 | Internal helpers › Database helpers | `sqliteFilename` …
`isJsonField` | 298 | 76 | 9 (2) | 0 | 0 | 0 (0) |
| 20072-20548 | Internal helpers › SQLite serialisation | `formatInput`
… `formatOutput` | 477 | 27 | 2 (0) | 0 | 0 | 0 (0) |
| 20549-20752 | Internal helpers › Introspection internals |
`introspectColumnOrder` … `introspectUniqueConstraints` | 204 | 73 | 5
(0) | 0 | 0 | 0 (0) |
| 20753-20827 | After the class | - | 75 | 0 | - | - | - | - |
### Pricing the areas
Eligibility is as cut 11 defined it: an annotated return type or not
public; no `getBuilder`, `this.FIELD.delete/insert/update(`,
private-static, `super`, bare-`this` or `this`-write site; not
`insertOnlyUpsertColumns`. 158 members are eligible on this base. Each
is priced as its stub minus its body, and the sum per section counts
every eligible body that has a negative price and no `sql-driver.ts#…`
pointer: Filter helpers -550 (22 bodies), the Rotation sub-banner -388
(15), CRUD -157 (12), every other section -93 or less. This cut then
priced what each area nets **once the module-level declarations it
shares with members that stay are kept in place** (⛔ no second copy of a
helper). The declarations that only the moved bodies read move with
them.
| Area (section) | Bodies | Bodies net | Helpers that move along |
Disposition |
|:--|--:|--:|--:|:--|
| **Canonical-encoding convergence** (the last 703 lines of the Rotation
sub-banner, 25 of them from the last 30 days) | 8 | **-193** | **-143**
| **Moves (this cut)** into the existing schema-sync module. -335
measured. |
| ↳ `migrateMysqlDatetimeColumns`, `migrateMysqlTimeColumns` (same
block) | 2 | -71 | (needs `isDeferredDdlLockWaitRefusal`) | **Stay.**
See "What stays" below. |
| Filter helpers: cross-field and `$now` comparison cluster
(`applyCrossFieldComparison`, `applyNowAnchorComparison`,
`crossFieldOffsetExpr`, `nowAnchorBaseExpr`) | 4 | -291 | -172 | Not
taken. Two of the helpers that would move
(`uncompilableFieldReferenceError`, `uncompilableNowAnchorError`) are
seam callers that `sql-driver-compile-refusal-seam.test.ts` enumerates
by parsing `sql-driver.ts` alone and pins equal to its DOORS. A new
module would be needed, and #26261 added `$now` there on 2026-10-26. |
| Rotation proper (`rotationShardKey`, `ensureRotation`,
`warnBuiltinColumnCollisions`, `rotationColumnList`) | 4 | -81 | 0 | Not
taken: it pays the least. `ensureShardTable` (-64) is named by path in
`check-object-def-param-keys.mjs`. |
| CRUD remainder (the upsert conflict-target path and small members) |
12 | -157 | -118 | Not taken. Spread across the section, and four of its
conflict-target declarations are shared with `upsert`.
`autoNumberValueExists` and `scanMaxNumericTail` hold 2 of the 4 D8
tenant predicates. |
## Why this area
- **It pays the most of any area that moves cleanly**, and its bodies
belong in an existing module. `sql-driver-schema-sync.ts` already
carries schema sync's drift pipeline (cuts 6, 8-10). The canonical
backfills and the MySQL widening are the rest of what `initObjects` runs
against a physical table.
- **Both files are anchored.** `scripts/adr-anchors/` has an entry for
`sql-driver.ts` and for `sql-driver-schema-sync.ts`, so ADR citations
inside the moved bodies stay on the anchored surface, and the
decision-letter count does not move (below).
- **What stays, and why.**
- `migrateMysqlDatetimeColumns` and `migrateMysqlTimeColumns` stay whole
in the class. They were part of the first plan. With them moved,
`check:durability-log-level` went **red** (exit 1). Each of them catches
around `runWideningAlters` (a vocabulary callee) and reports through
`this.logDurabilityFailure(…)`, and the gate resolves that helper only
when it is declared **in the same file** as the catch. In the module the
helper is not declared, so the gate read both catches as "swallows the
failure with no log at all". Keeping the two catches beside the helper
keeps the gate's reading exact: **48 seams at base and 48 at head, all
loud**. ⛔ No gate edit and no second copy of the helper.
- `withPinnedSession` (+4 if moved) and the `sqlite*Sql` expression
builders stay. The moved bodies reach them through the host.
- **Shared declarations, exported in place (cut 2/11 shape).**
`AUDIT_TIMESTAMP_COLUMNS`, `SQLITE_NON_TEMPORAL_TEXT_REFS`,
`SQLITE_TIME_EXPR_REFS`, `isMultiValuedColumn` and `LOCK_WAIT_REFUSAL`
are each also read by members that stay. `LOCK_WAIT_REFUSAL` is the
marker the moved `deferredDdlLockWaitError` sets and
`isDeferredDdlLockWaitRefusal` reads in the two migrate catches. Each
gains a leading `export` and nothing else, and the module imports them
back. The module reads them only inside function bodies, so the import
cycle is evaluation-safe. `index.ts` names its exports one by one; the
export lists below prove none of the five leaks.
- **No series limit is touched.** None of the eight builds through
`getBuilder`, reads a static, has a `this.FIELD.delete/insert/update(`
site, uses `super` or a bare `this`, or writes to `this`. All eight are
protected or private with annotated returns. `check:tenant-chokepoint`'s
D8 predicates, measured with its own `nullArmsOf`: **4 at base and 4 at
head**, all in `sql-driver.ts` (0 in the moved bodies).
- **Subclasses.** `TursoDriver` and `SqliteWasmDriver` override none of
the eight. The stubs keep their signatures, and every member the bodies
call is a late-bound closure on the instance.
- **Path readers, screened.** No script, test, doc or ADR names any of
the eight, or the four moved declarations, by a `sql-driver.ts#…`
pointer or a `sql-driver.ts::…` key. The five cli
`generate-*.pin.test.ts` files read `sql-driver.ts` text: the
`createColumn` switch, `DEFAULT_STRING_VARCHAR_CHARS`,
`MEDIA_ID_VARCHAR_CHARS`, `fieldHasColumn`, and `if
(isMultiValuedColumn(` inside `createColumn`, all unchanged. Four of
those files ran green at head (62 tests); the fifth sits in the cli
integration tier, which this run did not include and CI runs.
`logger-receiver-detach.test.ts` scans `sql-driver.ts`; the repo-wide
gate `check:logger-receiver-detach` walks every package (0 detaches at
both ends).
## The shape (#23050 shape B, as cuts 1-11 apply it)
- Each class method keeps its docblock and signature, byte-identical
less `async`. Its body is now one host literal plus `NAME.call(host,
...)`, marked `[#25521 cut 12]`, and returns the moved function's own
promise.
- Fields go as GETTERS that read the driver at call time:
`canonicalDatetimeFields`, `canonicalTimeFields`, `datetimeFields`,
`flushingDeferredDdl`, `jsonFields`, `timeFields` (new to the host), and
`isMysql`, `isSqlite`, `knex`, `logger` (already there). Methods go as
closures that call the method on the driver at call time. So a subclass
override, an instance spy, or a field replaced after construction is
still the one the body reaches (probe below).
- `SchemaSyncHost` grew by exactly the members these bodies read that it
did not list yet: the six fields above and `convergeJsonColumn`,
`countNonTemporalTextRows`, `legacyMysqlTimeColumns`,
`legacyMysqlTimestampColumns`, `runWideningAlters`,
`sqliteCanonicalDatetimeSql`, `sqliteCanonicalTimeSql`,
`sqliteNonTemporalTextSql` and `withPinnedSession`. Each is typed as the
class member's indexed access, inserted in the list's own order. There
is no cast, and no member becomes public.
- **Module imports.** `StandardErrorCode` (`@objectstack/spec/api`, for
the moved refusal), `recoverUnencodedJsonText`
(`./unencoded-json-text.js`) and the five shared declarations from
`./sql-driver.js`. The former `import type` line becomes one import with
`type` markers on `IntrospectedColumn` and `SqlDriver`.
- **Comments.** A `[#25521 cut 12]` paragraph in the module header says
what moved, what stayed and why. The `sql-driver.ts` import comment
names cut 12 in 2 lines. The header cites no `ADR-NNNN Dn`.
- **How the move was made.** A script produced it from the source text;
nothing was retyped. Each body line is re-indented two columns, **except
three lines that start inside a template literal**: the continuation
lines of the two MySQL `information_schema` queries. Those keep their
bytes, indentation included, because they are the SQL statement's own
text.
## ADR anchors
- **The count.** `check:adr-anchors` counts **1879** decision-letter
citations at base `83e9d7187` (a clean detached worktree) and **1879**
at head `ff5b18102`, over 90 anchored files at both ends. Both runs exit
0.
- **Why equal.** Every comment inside the moved bodies and declarations
moves from one anchored file to another.
- **No entry needs a new home.** The `sql-driver.ts` entry's ADR-0053
paragraph describes `formatOutput` and `presentReadValue`; the
schema-sync entry's ADR-0120 D4 paragraph describes the tightening
ceremony. Neither describes a moved body. No `scripts/adr-anchors/**`
file changed.
## The published `.d.ts` does not change
This is measured under the series bar (ruling `6348891160`, item 1).
- **How each end was built.** `pnpm turbo run build
--filter=@objectstack/driver-sql --force` at each end, against the same
dependency tree. The base leg writes base `sql-driver.ts` and
`sql-driver-schema-sync.ts` into this worktree, under an EXIT/INT/TERM
trap that restores HEAD. The restore is proved by the HEAD blob hashes
(`b38c9983`, `f191f7ca`), an empty `git diff HEAD` and 0 status entries.
- **The hashes.** The raw sha256 of `dist/index.d.ts` and
`dist/index.d.mts` is the same at base and at head,
`0f33bf3febaac3c7aa96f68a4226d752766b8203ab7acade523b69f9aaa44d74`
(unchanged since cut 8), and `cmp` finds them identical.
- **Positive control.** I dropped `protected` from
`backfillCanonicalDatetimes` in the head `index.d.ts`. The result hashes
to `b6627836855a2c0adf2bbed61211d8d467f5ce088f81f5299336f847d45d397b`,
with a 1-line diff and `cmp` exit 1.
- **Export lists.** CJS `require` and ESM `import` of the built dist
give 59 and 59 names at both ends, the same set (sha1 `05757ce5ae`).
None of the five newly exported declarations is among them.
- **The head dist carries the move.** `.call(host` sites in `index.mjs`
go from 35 at base to 43 at head.
## Move-only proof
The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes
skipped), base `83e9d7187` against head `ff5b18102`. It is a scratch
script and is not committed.
| Moved body | Body tokens | Comments | Parameter tokens less `this` |
Return type tokens | Body lines |
|:--|--:|--:|--:|--:|--:|
| `backfillCanonicalDatetimes` | 349 | 8 | 6 | 4 | 60 |
| `countNonTemporalTextRows` | 79 | 0 | 14 | 4 | 9 |
| `backfillCanonicalJsonEncoding` | 193 | 5 | 6 | 4 | 37 |
| `convergeJsonColumn` | 334 | 3 | 17 | 4 | 37 |
| `backfillCanonicalTimes` | 349 | 3 | 6 | 4 | 55 |
| `legacyMysqlTimestampColumns` | 211 | 1 | 11 | 15 | 20 |
| `runWideningAlters` | 224 | 7 | 19 | 4 | 40 |
| `legacyMysqlTimeColumns` | 205 | 0 | 11 | 15 | 20 |
- **Each moved body.** In every row, 0 tokens differ and every comment
is identical. `async` matches, and there are no type parameters. Each
body line is the base line less two leading spaces, or byte-identical
where it starts inside a template literal (1 line in
`legacyMysqlTimestampColumns`, 2 in `legacyMysqlTimeColumns`).
- **The eight stubs.** Leading trivia (the docblock), modifiers and the
signature are identical to base, less `async`.
- **The class.** It has 350 members at both ends, in the same order, and
the 342 unmoved members are byte-identical, trivia included.
- **Module-level declarations.** The four that moved are byte-identical
in the module, leading docblock included, and gone from `sql-driver.ts`.
The five shared ones differ from base by a leading `export` and nothing
else. At `sql-driver.ts`'s top level, 199 of 211 base statements are
byte-identical. The rest are the two edited imports, the five exports,
the four moved declarations and the class.
- **Result.** `PROOF OK`, exit 0.
- **Negative control.** I changed
`.limit(SQLITE_JSON_BACKFILL_PAGE_SIZE)` to
`.limit(SQLITE_JSON_BACKFILL_PAGE_SIZE + 1)` in a module copy (1 hit,
checked on disk). The proof then reports `convergeJsonColumn` with its
body tokens and lines differing, prints `PROOF FAILED`, and exits 1.
## Tests
Each end ran under one `os-verify-lock` hold, after building that end's
driver-sql dist. I compared the vitest JSON per test name and status,
base `83e9d7187` against head `ff5b18102`. Every part's exit code was
captured on its own (build, probe, three suites: all 0 at both ends).
| Suite | Base | Head | Lost / changed / added |
|:--|:--|:--|:--|
| `@objectstack/driver-sql` | 4060 (3846 passed, 214 skipped; 248 files)
| same | 0 / 0 / 0 |
| `@objectstack/driver-turso`, against that end's built driver-sql dist
| 2526 (2492 passed, 34 skipped; 95 files) | same | 0 / 0 / 0 |
| `@objectstack/driver-sqlite-wasm` | 746 (746 passed; 39 files) | same
| 0 / 0 / 0 |
- **Type check and lint.** `tsc --noEmit -p .` in driver-sql exits 0,
also after the merge. `pnpm exec eslint --no-inline-config
packages/drivers/driver-sql` exits 0.
- **Late-binding probe.** A scratch script, not committed. It runs node
against each end's built dist on in-memory SQLite. Its 45 readings print
identical output at base and head (`diff` is empty). They cover:
1. both SQLite backfills, reaching instance spies on
`countNonTemporalTextRows` and the three `sqlite*Sql` builders, with
`canonicalDatetimeFields` and `canonicalTimeFields` replaced after
construction (the certified sets land in the replaced objects);
2. a SUBCLASS override of `countNonTemporalTextRows`, reached with its
arguments (the withheld-rows warning follows), and `datetimeFields`
emptied after construction (nothing scanned);
3. the JSON backfill, reaching an instance spy on `convergeJsonColumn`,
then a subclass override that throws (warned, not thrown), and
`jsonFields` emptied after construction;
4. the MySQL path on a driver whose `isMysql` is shadowed on the
instance and whose `knex.raw`, `withPinnedSession`, `nowColumnDefault`,
`runWideningAlters` and `logDurabilityFailure` are replaced on the
instance:
- the two column reads;
- both migrations' ALTER statements;
- the lock-wait bound set and restored around the ALTER;
- outside a flush, the raw lock error;
- with `flushingDeferredDdl` flipped on the instance, the
`DATABASE_ERROR`/500 refusal. `migrateMysqlTimeColumns` (in the class)
recognises it through the exported `LOCK_WAIT_REFUSAL` and rethrows it.
Outside a flush the failure goes to `logDurabilityFailure`;
5. bad arguments on each of the ten methods.
- **Not run.** No ablation, because no tested behaviour changed. The two
migrate methods did not move.
- **`test:repo`.** The `repo` projects of types, core, rest and objectql
pass locally. Runtime's two files could not load
(`@objectstack/service-datasource` has no dist in this container), so
that one is NOT MEASURED locally. CI's Test Core runs all of them.
## Gates
- **Derived list.** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` derives 53 commands for the 2 changed
paths (merge base `83e9d7187`). On `ff5b18102`, 52 exit 0.
`check:dual-build-cjs-loads` exits 3, `PREREQUISITE NOT MET` (no full
build here): **NOT MEASURED** locally, CI runs it. The teardown
self-test's pinned commit was fetched first, and objectql was built for
`check:lean-entry-closure`.
- **Reconciliation.** `dispatch-gates --ran`: 53 derived families
accounted for, 52 run, 1 NOT-MEASURED (derived from the recorded exit
3), exit 0. After the merge the derived list is still 53 commands.
- **Card-named gates, read at base (a clean detached worktree at
`83e9d7187`) and at head, all exit 0:**
| Gate | Base | Head |
|:--|:--|:--|
| `check:adr-anchors` | 90 files, 1879 decision-letter citations | 90
files, 1879 |
| `check:adr-symbol-anchors` | 2664 resolve | 2664 resolve |
| `check:tenant-chokepoint` | 24 bindings / 3 files, D8 clean; 4 tenant
predicates | same; 4 tenant predicates |
| `check:durability-log-level` | 48 seams, all loud (7 propagating) | 48
seams, all loud (7 propagating) |
| `check:driver-conformance` | 55 covered / 0 DEBT | same |
| `check:keyed-text-bounds` | 112 files / 238 index entries | same |
| `check:object-def-param-keys` | 3310 files / 542 classes / 7 positions
| same |
| `check:system-context-census` | 153 sites / 132 symbols | same |
| `check-tenant-audit-census` | 243 sites | same |
| `check:logger-receiver-detach` | 0 detaches, 3750 files | 0 detaches,
3751 files (the head worktree also holds the build-generated,
git-ignored `packages/spec/src/migrations/registry.ts`) |
- `check:nul-bytes` passes. The pre-push `check:commit-card-trailers`
passed on push.
## Acceptance notes
- **The #9354 lock-wait family now spans two files.** The bound, its
recognizer and the refusal builder (`deferredDdlLockWaitError`) moved
with `runWideningAlters`. The marker `LOCK_WAIT_REFUSAL` and its reader
`isDeferredDdlLockWaitRefusal` stay in `sql-driver.ts` with the two
migrate catches. `isDeferredDdlLockWaitRefusal`'s docblock links `{@link
deferredDdlLockWaitError}`, which is now in the module. Left as is: the
cut is move-only.
- **`check:durability-log-level` resolves loud helpers by file.** The
gate reads `this.logDurabilityFailure(…)` as loud only when that helper
is declared in the same file as the catch. Any later cut that moves a
catch around a vocabulary callee out of `sql-driver.ts` meets the same
red. Noted, not filed: the gate is right to refuse an unresolved callee,
and changing its resolver would be a gate change.
- **Prose that names a moved body by file.** ADR-0053 names
`backfillCanonicalDatetimes` and `backfillCanonicalTimes` without a
path. `TursoDriver`'s docblocks (`remote-canonical-backfill.ts`,
`remote-codec-residue-backfill.ts`, `turso-driver.ts`) name
`SqlDriver.backfillCanonicalDatetimes`, `backfillCanonicalTimes`,
`backfillCanonicalJsonEncoding` and `countNonTemporalTextRows`. Each is
still a member of `SqlDriver`. No `docs/adr/**` edit in this series.
- **Next slice, priced on this base.** Filter helpers' cross-field and
`$now` cluster pays about -460 gross. It needs
`sql-driver-compile-refusal-seam.test.ts`'s enumeration migrated to read
the receiving module, and a new seam module. The CRUD upsert
conflict-target path needs four shared declarations exported in place.
---
_Generated by [Claude
Code](https://claude.ai/code/session_011aqraB3u125sQzkMQNboVa)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_011aqraB3u125sQzkMQNboVa
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4548ba3 commit 8b39b77
2 files changed
Lines changed: 601 additions & 437 deletions
0 commit comments