Skip to content

Commit 659f2d9

Browse files
refactor(driver-sql): move the read exits' fault terminals out of sql-driver.ts into sql-driver-read-exit-fault.ts (#25521 cut 15) (#26490)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XWfewWfvar93yD4Po1GfCr7)_ Part of #25521 Clause-②: no Metadata-protocol: none Ruling-ref: 6270097840 Ruling-ref: 6348891160 ## 维护者速读 - **改了什么**:把 `sql-driver.ts` 里「读操作被数据库拒绝时怎么回答」的整组代码(`find`/`count` 的兜底故障信封、WHERE 列找不到时的拒绝、`aggregate` 与 `distinct` 各自的分类器和列拒绝,以及只被它们用到的四个模块级函数)原样搬进新文件 `sql-driver-read-exit-fault.ts`;类上的六个方法全部保留,只把方法体转交过去。 - **为什么改**:#25521 拆分超大文件的第 15 刀;这组是剩余可搬区域里收益最大的一块,`sql-driver.ts` 减少 445 行。 - **风险与回滚**:纯搬移,无行为变化;三个 SQL 驱动的测试两端逐条一致,公开类型声明哈希不变。回滚即还原本 PR。 - **先看**:`sql-driver-read-exit-fault.ts` 的头注释与 `ReadExitFaultHost`;`sql-driver.ts` 中六个方法的转交桩;`sql-driver-compile-refusal-seam.test.ts` 新增的那一个扫描文件。 - **两分钟验证**:`pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-backend-fault-envelope.test.ts src/sql-driver-compile-refusal-seam.test.ts src/sql-driver-unresolvable-where-column-refusal.test.ts`,预期全部通过。 - **需要你做的**:无,席位落地。 ## What changes Cut 15 of the `sql-driver.ts` split (#24586 ruling A, recorded in `6270097840`). The claim is `6432488188`. The cut opens with a fresh seam table (below), taken on `origin/main` `d1a2e71a3` (cut 14's merge), and takes the area that pays best without breaking a series limit: the **fault terminals of the read exits**, what a read answers when the backend refused its statement. It moves unedited into a **new** seam module, `packages/drivers/driver-sql/src/sql-driver-read-exit-fault.ts`, through a new host `ReadExitFaultHost`: - `backendStatementFault`, the #8931 terminal that `find` and `count` end in; - `unresolvableFilterColumnRefusal`, #8790's `INVALID_FILTER` for a WHERE column the database could not resolve; - `aggregateBackendFault` (#11455 / #11541) and `distinctBackendFault` (#17639 / #17857), the classifiers of `aggregate` and `distinct`, each of which ends in one of the two above or in its own column refusal; - `unresolvableAggregateColumnRefusal` and `unresolvableDistinctColumnRefusal`, those two column refusals. With them go the four module-level declarations only they read: the #8931 envelope `backendStatementFaultError`, #8790's `unresolvableFilterColumnError`, the dialect-message reader `unresolvableColumnNameOf` and the #20020 attribution walk `unresolvableColumnProvenance`. The `[#21385]` import of `redactStatementFromMessage` follows its last reader into the module, with its own comment. 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. Two tests change, and only as path readers (below). Branch `claude/project-thread-uliqpv` (the platform assigned it to this card thread, as the claim records). The move is one commit, `ef0640e10`, on `d1a2e71a3`. **Net line count (the seat's rule).** `sql-driver.ts` goes from 19,794 lines to 19,349: **-445**. - **Out:** the six bodies, 189 lines (15 + 87 + 23 + 25 + 16 + 23). The four declarations, 293 lines with their own docblocks (144 + 54 + 41 + 54), plus 4 separator lines. The `redactStatementFromMessage` import and its 6-line comment, 7 lines. Two names (`declareTargetedTable`, `isMissingTableError`) from the `@objectstack/types` import, 2 lines. - **In:** the six stub bodies, 46 lines (8 + 12 + 7 + 6 + 7 + 6), and a 4-line import of the module with its comment. - The new module is 607 lines. **Path readers migrated in the same commit (both tests, no assertion changed).** - `sql-driver-compile-refusal-seam.test.ts` enumerates every `INVALID_FILTER` builder by parsing a fixed list of source files, and asserts `unresolvableFilterColumnError` is one of the three direct callers. That function now lives in the new module, so the list gains it (one line, plus its comment and the two docblocks that name the list). The direct-caller set it asserts is unchanged. - `sql-driver-unresolvable-where-column-refusal.test.ts` imports `unresolvableColumnNameOf` from the new module instead of `./sql-driver.js`. **Serial with the file's in-flight PRs.** I read the file list of every open PR (23, by fetching each head and diffing it against its merge base with `origin/main`) before the first edit. None touches `sql-driver.ts`, `turso-driver.ts`, `sqlite-wasm-driver.ts`, `check-tenant-chokepoint.mjs` or the driver-sql anchor entries. #26138 touches `driver-sql/src/builtin-column-collision.ts` only; #26337 adds `driver-sql/src/sql-driver-external-unprovisioned-sort-anchor.test.ts` only, which reads none of the moved members. Re-measured before `pr_create` (21 open PRs): #26451 edits the five drivers' `package.json` only, and #26337 is as above; neither touches this region. ## Seam table, fresh, on `d1a2e71a3` - **Size and churn.** `sql-driver.ts` is 19,794 lines. The 30-day column counts `git blame` lines from commits on or after 2026-09-27T03:11:08Z, 30 days before the base commit. History reaches 2026-09-18. `scripts/pm/git-history.mjs count` proves 75 commits on the file in 30 days and 33 in the last 7. In all, 3,481 lines (17.6%) are from the last 30 days. - **Class shape.** `SqlDriver` spans lines 5149-19719 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-14 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-5148 | Module preamble | - | 5,148 | 793 | - | - | - | - | | 5149-6740 | Class head: fields, statics, constructor, capability getters | `name` … `assertDialectEmits` | 1,592 | 290 | 8 (1) | 1 | 0 | 18 (18) | | 6741-7172 | Lifecycle | `connect` … `disconnect` | 432 | 155 | 8 (3) | 0 | 0 | 6 (6) | | 7173-9579 | CRUD — IDataDriver core | `find` … `delete` | 2,407 | 745 | 43 (7) | 0 | 12 | 0 (0) | | 9580-10191 | Bulk & Batch Operations | `bulkCreate` … `count` | 612 | 119 | 9 (6) | 0 | 7 | 0 (0) | | 10192-10288 | Raw Execution | `rawStatementFault` … `noteRuntimeIndexDdl` | 97 | 28 | 3 (1) | 0 | 0 | 0 (0) | | 10289-10381 | Transactions | `beginTransaction` … `rollbackTransaction` | 93 | 46 | 7 (5) | 0 | 0 | 0 (0) | | 10382-10913 | Aggregation | `aggregate` … `unresolvableAggregateColumnRefusal` | 532 | 60 | 5 (1) | 0 | 1 | 0 (0) | | 10914-11201 | Distinct | `distinct` … `unresolvableDistinctColumnRefusal` | 288 | 17 | 3 (1) | 0 | 1 | 0 (0) | | 11202-11317 | Window Functions | `findWithWindowFunctions` … `findWithWindowFunctions` | 116 | 4 | 1 (1) | 0 | 1 | 0 (0) | | 11318-11416 | Query Plan Analysis | `explain` … `analyzeQuery` | 99 | 1 | 2 (2) | 0 | 1 | 0 (0) | | 11417-11475 | Schema Sync (syncSchema / init) | `syncSchema` … `reclaimSpace` | 59 | 34 | 3 (3) | 0 | 0 | 0 (0) | | 11476-13024 | Schema Sync (syncSchema / init) › Data-lifecycle rotation (ADR-0057 P2) | `rotationStateByTable` … `migrateMysqlTimeColumns` | 1,549 | 158 | 26 (4) | 0 | 0 | 0 (0) | | 13025-13281 | Schema Sync (syncSchema / init) › Deferred schema DDL (#3917) | `setDeferredDdl` … `flushDeferredSchemaDdl` | 257 | 25 | 6 (4) | 0 | 0 | 0 (0) | | 13282-14096 | Schema Sync (syncSchema / init) › Managed-schema drift & reconcile (#2186) | `dialectName` … `createNullSafeUniqueIndex` | 815 | 237 | 23 (3) | 0 | 0 | 0 (0) | | 14097-14148 | Schema Introspection | `listPhysicalColumns` … `introspectSchema` | 52 | 46 | 2 (2) | 0 | 0 | 0 (0) | | 14149-14419 | Internal helpers | `getKnex` … `auditMissingTenant` | 271 | 73 | 8 (1) | 0 | 1 | 0 (0) | | 14420-16569 | Internal helpers › Filter helpers | `tableNameForBuilder` … `applyFalseConstant` | 2,150 | 354 | 44 (2) | 0 | 0 | 0 (0) | | 16570-16745 | Internal helpers › Field mapping | `mapSortField` … `mapAggregateFunc` | 176 | 2 | 6 (0) | 0 | 0 | 0 (0) | | 16746-16793 | Internal helpers › Window function builder | `buildWindowFunction` … `buildWindowFunction` | 48 | 26 | 1 (0) | 0 | 0 | 0 (0) | | 16794-18740 | Internal helpers › Column creation helper | `nowColumnDefault` … `applyDeclaredColumnDefault` | 1,947 | 92 | 19 (0) | 0 | 0 | 8 (8) | | 18741-19038 | Internal helpers › Database helpers | `sqliteFilename` … `isJsonField` | 298 | 76 | 9 (2) | 0 | 0 | 0 (0) | | 19039-19515 | Internal helpers › SQLite serialisation | `formatInput` … `formatOutput` | 477 | 27 | 2 (0) | 0 | 0 | 0 (0) | | 19516-19719 | Internal helpers › Introspection internals | `introspectColumnOrder` … `introspectUniqueConstraints` | 204 | 73 | 5 (0) | 0 | 0 | 0 (0) | | 19720-19794 | After the class | - | 75 | 0 | - | - | - | - | ### Pricing the areas Eligibility is as cuts 11-14 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`. 141 members are eligible on this base. Each area is priced as its stubs minus its bodies, then net of the module-level declarations it shares with members that stay (they stay in place, exported; ⛔ no second copy of a helper), counting only a declaration's own docblock (cut 14's correction: the move script refuses a declaration whose leading trivia is not exactly one JSDoc block directly above it, and none of the four had another comment). | Area | Bodies | Bodies net | Declarations that move along | Disposition | |:--|--:|--:|--:|:--| | **Read-exit fault terminals** (`backendStatementFault`, `unresolvableFilterColumnRefusal`, `aggregateBackendFault`, `distinctBackendFault`, `unresolvableAggregateColumnRefusal`, `unresolvableDistinctColumnRefusal`) | 6 | **-143** | **-297** (the four above with docblocks and separators), -5 net for imports | **Moves (this cut).** -445 measured. | | `backendStatementFault` alone (cut 14's priced next slice) | 1 | -75 | -145 (`backendStatementFaultError`), +4 for the import | Subsumed: about -216, the first row's largest part. Taken alone it leaves its three callers' siblings behind, and `aggregateBackendFault`/`distinctBackendFault` call it. | | Rotation sub-banner (`ensureRotation`, `registerExternalObject`, `warnBuiltinColumnCollisions`) | 3 | about -118 | 0 | Not taken: `migrateMysqlDatetimeColumns`/`migrateMysqlTimeColumns` stay (cut 12 durability trap), `ensureShardTable` is named by `check-object-def-param-keys.mjs` and `computeTenantField` by `packages/spec/liveness/object.json`. | | The rest of the Filter helpers (`compileFilters` -75, `applyNormalizedComparison` -60, …) | - | - | - | Not priced as one area: small bodies spread around `applyFilterCondition`, which an ADR-0139 pointer pins in place. | ## Why ### Why this area, as one area - **One mechanism.** Every read exit catches its statement's failure and hands it to this family: `findRows` and `count` to `unresolvableFilterColumnRefusal` or the `backendStatementFault` terminal; `aggregate` to `aggregateBackendFault`, which ends in its column refusal, the WHERE refusal or the terminal; `distinct` to `distinctBackendFault`, the same shape. The six bodies only classify an error the exit already caught and compose the envelope; none runs a statement. They sit under three banners (Bulk, Aggregation, Distinct) only because each sits next to the exit that calls it. - **It pays the most of any area that moves cleanly**, about twice the lone terminal. - **What stays, and why.** - The exits themselves (`findRows`, `count`, `aggregate`, `distinct`) build through `getBuilder`, and stay. - `isUnresolvableColumnError` (also read by `findRows` and `count`), `OWN_STATEMENT_FAULT` (read by `sql-driver-raw-execution.ts`, which imports it from `sql-driver.ts`), `PRE_DDL_QUESTION_SCOPE` (also run by the two media-arm resolvers), `unsupportedFilterError` and `isFilterNode` (each read by much of the filter compiler) are shared. The three that were not exported gain a leading `export` and nothing else, and the module imports all five 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 them leaks. - `mapSortField` and `remoteColumn` are read by members that stay too; the moved bodies reach them through the host. - **No series limit is touched.** None of the six builds through `getBuilder`, reads a static, has a `this.FIELD.delete/insert/update(` site, uses `super` or a bare `this`, or writes to `this`. Every one has an annotated return type and is protected. `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 (none reads a tenant field). - **Durability gate.** The moved code has no `catch` clause (the word appears only in comments: one docblock heading and two `catch-all` notes). `check:durability-log-level --list` gives the same seams at both ends, line numbers aside; the gate reads 48 seams (7 propagating) and 82 read seams at both ends. - **Logger.** Four bodies write `this.logger.warn(` / `this.logger.debug?.(`; the host carries `logger` as a getter, so the receiver is the driver's logger as before. `check:logger-receiver-detach`: 0 detaches at both ends. - **Subclasses.** `TursoDriver` calls `aggregateBackendFault` and `distinctBackendFault` on its own instance; `sql-driver-21385-refusal-log-line-redaction.test.ts` overrides four of the six in a subclass and calls `super`. Every stub keeps its signature and stays `protected`, and every member a body calls is a late-bound closure on the instance (probe below). - **Path readers, screened.** No script, test, doc or ADR names any of the six, or the four moved declarations, by a `sql-driver.ts#…` pointer or a `sql-driver.ts::…` key, and no `scripts/adr-anchors/**` record names them. Tests that read `sql-driver.ts` as text: the five cli generate pins (they read `createColumn`, which stays), `logger-receiver-detach.test.ts` (it scans for detach shapes, finds none either way), and the compile-refusal seam test (migrated above). Prose mentions elsewhere (`turso-driver.ts`, `engine.ts`, the runtime noise filters, the types predicates' tests, CHANGELOGs) name the functions, and each is still true. ## The shape (#23050 shape B, as cuts 1-14 apply it) - Each class method keeps its docblock and signature, byte-identical. Its body is now one host literal plus `NAME.call(host, ...)`, marked `[#25521 cut 15]`. - Fields go as GETTERS that read the driver at call time (`logger`, `physicalTableByObject`, `deferredDdl`, `deferredSchemaObjects`). Methods go as closures that call the method on the driver at call time (`mapSortField`, `remoteColumn`, `backendStatementFault`, and the three refusals). So a subclass override, an instance spy, or a field replaced after construction is still the one the body reaches. - `ReadExitFaultHost` lists exactly those ten members, each typed as the class member's indexed access. There is no cast, and no member becomes public. - **Module imports.** `StandardErrorCode` (`@objectstack/spec/api`), `type DriverQuery` (`@objectstack/spec/contracts`), `resolveFilterSubtreeProvenance` (`@objectstack/spec/data`, still read in `sql-driver.ts` too), `declareTargetedTable` and `isMissingTableError` (`@objectstack/types`, whose last readers in `sql-driver.ts` moved, so they leave its import), the moved `redactStatementFromMessage` import, and from `./sql-driver.js` the five shared declarations plus `type SqlDriver`. - **Comments.** The module header says what moved, what stayed and why. The `sql-driver.ts` import comment names cut 15 in 3 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; no body line starts inside a template literal. ## Verification ### ADR anchors - **The count.** `check:adr-anchors` counts **1876** decision-letter citations at base `d1a2e71a3` (a clean detached worktree) and **1876** at head, over 91 anchored files at both ends. Both runs exit 0. - **Why equal.** No comment inside the moved bodies, declarations or import cites a decision letter. The stubs keep their docblocks, so every `ADR-…` mention in them stays in `sql-driver.ts`. - **No entry needs a new home.** The `sql-driver.ts` entry's one paragraph (ADR-0053) describes `formatOutput` and `presentReadValue`, neither of which moved. `check:adr-symbol-anchors`: 2664 resolve at both ends. ### 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 the two base test files into this worktree and deletes the new module, under an EXIT/INT/TERM trap that restores HEAD. The restore is proved by the HEAD blob hashes (`4aeac25e`, `dc34f18e`, and both tests), 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). - **Positive control.** I dropped `protected` from `backendStatementFault` in the head `index.d.ts`. The result hashes to `2e64636ebed0f4461e048dc1d3fa09a07c4172c537607d7024a2b36038eee409`, with a 1-line diff and `cmp` exit 1. - **Export lists.** CJS `require` of the head dist gives 59 names (sha1 `05757ce5ae`); the ESM export statement of the base and head `index.mjs` lists the same 59 names (sha1 `05757ce5ae` both). None of the three newly exported declarations, and nothing from the new module, is among them. - **The head dist carries the move.** `.call(host` sites in `index.mjs` go from 51 at base to 57 at head (six stubs). ### Move-only proof The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base `d1a2e71a3` against head `ef0640e10`. It is a scratch script and is not committed. | Moved body | Body tokens | Comments | Parameter tokens less `this` | Return type tokens | Body lines | |:--|--:|--:|--:|--:|--:| | `unresolvableFilterColumnRefusal` | 136 | 0 | 10 | 1 | 15 | | `backendStatementFault` | 245 | 44 | 6 | 1 | 87 | | `aggregateBackendFault` | 156 | 7 | 9 | 1 | 23 | | `unresolvableAggregateColumnRefusal` | 161 | 0 | 20 | 1 | 25 | | `distinctBackendFault` | 69 | 6 | 13 | 1 | 16 | | `unresolvableDistinctColumnRefusal` | 134 | 4 | 9 | 1 | 23 | - **Each moved body.** In every row, 0 tokens differ and every comment is identical. Each body line is the base line less two leading spaces. None is `async`, at either end. - **The six stubs.** Leading trivia (the docblock), modifiers and the signature are identical to base. - **The class.** It has 350 members at both ends, in the same order, and the 344 unmoved members are byte-identical, trivia included. - **Module-level declarations.** The four that moved are byte-identical in the module from their own docblock on, and gone from `sql-driver.ts`. The three newly exported ones differ from base by a leading `export` and nothing else. The `redactStatementFromMessage` import moved whole with its comment (7 lines). At `sql-driver.ts`'s top level, 189 of 199 base statements are byte-identical; the rest are the four moved declarations, the three exports, the moved import, the `@objectstack/types` import (less two names) and the class. The one head-only statement besides those is the new import. - **Result.** `PROOF OK`, exit 0. - **Negative control.** Through `scripts/ablation-replace.mjs`, I changed `typeof declared === 'number'` to `'string'` in the module's `backendStatementFault` (1 hit, checked on disk). The proof then reports `backendStatementFault` with its body tokens differing, prints `PROOF FAILED`, and exits 1. The restore was proved (blob equals HEAD, empty `git diff HEAD`). ### 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 `d1a2e71a3` against head `ef0640e10`. 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. `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 and prints 48 readings, each with the envelope's message, `code`, `status`, `cause`, own keys and symbol-keyed members (the declared target table), the log lines written, and the instance spies hit in order: 1. `backendStatementFault` on a declared error (returned as the same object), five undeclared ones, a federated object (the declared target follows `physicalTableByObject`), and the three deferred-DDL cases (demoted to `debug`, warned, warned); 2. `unresolvableFilterColumnRefusal` with no filter, an author-marked, an unmarked and a policy-marked filter, and an unnamed column; 3. `aggregateBackendFault` on a groupBy column (string and structured), an aggregation column, both, a WHERE column, an unnamed column and another fault; 4. `distinctBackendFault` on the listed field, a filter column, an unnamed column and another fault; 5. the two column refusals directly, on every `namedBy` shape; 6. a SUBCLASS overriding the four refusal/terminal methods, `mapSortField` and `remoteColumn`, each reached through the classifiers; `logger`, `physicalTableByObject`, `deferredDdl` and `deferredSchemaObjects` replaced on the instance after construction (the new ones are the ones read); 7. eight `find`/`count`/`aggregate`/`distinct` calls end to end on SQLite, on a missing table and on a dropped column; 8. bad arguments on three of the methods. - **Probe result.** 33 of the 48 readings are identical, envelopes and log lines included. The other 15 differ only in the top stack frame's receiver name, where the error is constructed inside a moved body: `_SqlDriver.X` (or `Sub.X`) at base and `Object.X` at head. With that receiver name normalised, the two outputs are byte-identical. That is the host-literal receiver of shape B, the same at every earlier cut, and no test or consumer reads it. - **Not run.** No ablation, because no tested behaviour changed. - **`test:repo`.** After the merge of `origin/main` (`3b5318a22a`), each under the lock after building its closure: the `repo` projects of types (11 passed), core (83), objectql (5), spec (1293), rest (280 passed, 18 skipped) and runtime (947) all pass, exit 0. ### Gates - **Merge of `origin/main`.** `3b5318a22a` merges 9 commits that touch no `packages/drivers/**` file and no gate this cut reads. After it, `tsc --noEmit -p .` in driver-sql exits 0 and `check:adr-anchors` still reads 1876. - **Derived list.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 54 commands for the changed paths, the same 54 before and after the merge. On `3b5318a22a`, 53 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`: 54 derived families accounted for, 53 run, 1 NOT-MEASURED (derived from the recorded exit 3), exit 0. - **Card-named gates, read at base (a clean detached worktree at `d1a2e71a3`) and at head (`ef0640e10`; the derived ones again on the merged head `3b5318a22a`), all exit 0:** | Gate | Base | Head | |:--|:--|:--| | `check:adr-anchors` | 91 files, 1876 decision-letter citations | 91 files, 1876 | | `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); 82 read seams | same; `--list` equal but for line numbers | | `check:driver-conformance` | 55 covered / 0 DEBT | same | | `check:keyed-text-bounds` | 112 files / 238 index entries | same | | `check:object-def-param-keys` | 3329 files / 545 classes / 7 positions | 3331 files on the merged head (the new module, plus what `main` brought) / 545 / 7 | | `check:system-context-census` | 153 sites / 132 symbols | same | | `check-tenant-audit-census` | 244 sites | same | | `check:logger-receiver-detach` | 0 detaches | 0 detaches (one more file walked) | - `check:nul-bytes` passes. `check-platform-checklist --changed-from origin/main` (a moved exported function) passes: 6 anchors judged, 0 lost. The pre-push hooks passed on push. ## Acceptance notes - **`OWN_STATEMENT_FAULT` has no reader left in `sql-driver.ts`.** Its readers in the file were the three column refusals and the terminal, all moved; `sql-driver-raw-execution.ts` and the new module import it from there. It stays where it is: moving it would edit the raw-execution module's import for no line it pays, and one home is kept either way. The raw-execution header's phrase "`OWN_STATEMENT_FAULT` has readers that did not move" now means readers outside that module. Noted, not filed. - **`check:tenant-chokepoint`'s `SCAN_FILES` does not list the new module.** The moved bodies hold 0 of the file's 4 tenant predicates and build no query. Noted, not filed: widening `SCAN_FILES` would be a gate change. - **Next slice, priced on this base.** The rotation sub-banner is now the largest clean area (`ensureRotation`, `registerExternalObject`, `warnBuiltinColumnCollisions`, about -118, with the durability-trap and path-reader members staying). The series' remaining yield outside the pinned methods (`createColumn`, `applyFilterCondition`, `formatInput`/`formatOutput`, `initObjects`) is small bodies of under -70 each. ## Human Minimum - [x] 1. No `packages/spec` change. - [x] 2. The one new name, `ReadExitFaultHost`, follows the interface casing. - [x] 3. No formatter ran; scoped `eslint --no-inline-config` passes (repo-wide `pnpm lint` is CI's run). - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. No publishable behaviour change: `skip-changeset`; both declaration lines above. - [x] 6. `packages/spec` not touched. - [x] 7. No hand edits to generated docs, changelogs or release notes. - [x] 8. Title, body and comments are in English (the maintainer brief excepted). --- _Generated by [Claude Code](https://claude.ai/code/session_01FEkQ5mL6cyUGiP87zTtR7y)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FEkQ5mL6cyUGiP87zTtR7y Co-authored-by: Claude <noreply@anthropic.com>
1 parent 00874b9 commit 659f2d9

4 files changed

Lines changed: 670 additions & 502 deletions

File tree

‎packages/drivers/driver-sql/src/sql-driver-compile-refusal-seam.test.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
* ## Two halves, and why both
2929
*
3030
* 1. **The enumeration** reads `sql-driver.ts` and its cross-field seam module
31-
* `sql-driver-cross-field-comparison.ts` (#25521 cut 13) and lists every
31+
* `sql-driver-cross-field-comparison.ts` (#25521 cut 13), plus the read-exit
32+
* fault module `sql-driver-read-exit-fault.ts` (#25521 cut 15), and lists every
3233
* function that builds an `INVALID_FILTER` error. Only three may call
3334
* `unsupportedFilterError` directly (the seam itself, the `'author'`
3435
* re-issue, and the column refusal raised from a dialect error AFTER the
@@ -272,8 +273,8 @@ const DOORS: readonly Door[] = [
272273
// ── Half 1: the enumeration ───────────────────────────────────────────────────
273274

274275
/**
275-
* Every function in `sql-driver.ts` and `sql-driver-cross-field-comparison.ts`
276-
* that builds an `INVALID_FILTER` error, by the route it takes. Read with the TypeScript parser, not a regex, so a call
276+
* Every function in `sql-driver.ts`, `sql-driver-cross-field-comparison.ts` and
277+
* `sql-driver-read-exit-fault.ts` that builds an `INVALID_FILTER` error, by the route it takes. Read with the TypeScript parser, not a regex, so a call
277278
* split across lines, an inline `throw`, or a builder written as a method or an
278279
* arrow cannot slip past the count.
279280
*/
@@ -284,9 +285,12 @@ function enumerateRefusalBuilders(): {
284285
} {
285286
// [#25521 cut 13] The cross-field and now-anchored comparison compile moved to
286287
// its own seam module with two of the builders below; the scan reads both.
288+
// [#25521 cut 15] The read exits' fault terminals moved to a third, with
289+
// `unresolvableFilterColumnError`; the scan reads it too.
287290
const sources = [
288291
['sql-driver.ts', readFileSync(new URL('./sql-driver.ts', import.meta.url), 'utf8')],
289292
['sql-driver-cross-field-comparison.ts', readFileSync(new URL('./sql-driver-cross-field-comparison.ts', import.meta.url), 'utf8')],
293+
['sql-driver-read-exit-fault.ts', readFileSync(new URL('./sql-driver-read-exit-fault.ts', import.meta.url), 'utf8')],
290294
] as const;
291295
const directCallers = new Set<string>();
292296
const seamCallers = new Set<string>();

0 commit comments

Comments
 (0)