Skip to content

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

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-uliqpv
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-uliqpv

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

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;类上的六个方法全部保留,只把方法体转交过去。
  • 为什么改:[maintainer] refactor(driver-sql): split src/sql-driver.ts (22,096 lines, +2,533 in 30 days) along its SqlDriver seams, one move-only slice per PR #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:

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

  • 1. No packages/spec change.
  • 2. The one new name, ReadExitFaultHost, follows the interface casing.
  • 3. No formatter ran; scoped eslint --no-inline-config passes (repo-wide pnpm lint is CI's run).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. No publishable behaviour change: skip-changeset; both declaration lines above.
  • 6. packages/spec not touched.
  • 7. No hand edits to generated docs, changelogs or release notes.
  • 8. Title, body and comments are in English (the maintainer brief excepted).

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5mL6cyUGiP87zTtR7y

…-driver.ts into sql-driver-read-exit-fault.ts

Move-only. backendStatementFault, unresolvableFilterColumnRefusal,
aggregateBackendFault, distinctBackendFault and the aggregate and distinct
column refusals move, unedited, into a new seam module behind
ReadExitFaultHost, with the four module-level declarations only they read
(backendStatementFaultError, unresolvableFilterColumnError,
unresolvableColumnNameOf, unresolvableColumnProvenance) and the
redactStatementFromMessage import with its comment. The class keeps every
method as a stub with its docblock and signature. PRE_DDL_QUESTION_SCOPE,
unsupportedFilterError and isFilterNode gain export in place. The
compile-refusal seam test scans the new module too, and the
unresolvable-where test imports unresolvableColumnNameOf from it.

Claude-Session: https://claude.ai/code/session_01FEkQ5mL6cyUGiP87zTtR7y
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-sql, touching 32 documentable anchor(s).

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx (via SQLITE_ERROR (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/drivers.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/data-modeling/external-datasources.mdx (via remoteColumn (symbol, a field of interface ReadExitFaultHost), remoteColumn (literal, a string literal in remoteColumn; a string literal in unresolvableFilterColumnRefusal))
  • content/docs/data-modeling/index.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/data-modeling/queries.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/permissions/tenant-audit-census.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/plugins/packages.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/protocol/kernel/index.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/protocol/kernel/lifecycle.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/protocol/objectql/query-syntax.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/protocol/objectql/types.mdx (via SqlDriver (symbol, a top-level type))

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

  • content/docs/releases/v17/17-0.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/releases/v17/17-5.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/releases/v17/17-6.mdx (via SqlDriver (symbol, a top-level type))
  • content/docs/releases/v18/18-1.mdx (via SqlDriver (symbol, a top-level type))

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

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

Coarse fallback — 13 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 d0a7e748a933a7fb549c38c3105c1c49f5935dec → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 77670858e8aff44fe7da5e2a5bc89cce3e2b847a — the merge of head 3b5318a22a8ca7614fcf4471689f14bf2acac744 into base d0a7e748a933a7fb549c38c3105c1c49f5935dec, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 77670858e8aff44fe7da5e2a5bc89cce3e2b847a && git checkout 77670858e8aff44fe7da5e2a5bc89cce3e2b847a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d0a7e748a933a7fb549c38c3105c1c49f5935dec 3b5318a22a8ca7614fcf4471689f14bf2acac744 && git checkout -B drift-repro d0a7e748a933a7fb549c38c3105c1c49f5935dec && git merge --no-ff 3b5318a22a8ca7614fcf4471689f14bf2acac744

node scripts/docs-audit/affected-docs.mjs --json d0a7e748a933a7fb549c38c3105c1c49f5935dec

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-27T05:31Z · head 3b5318a22a · #25521 cut 15

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

  1. Shape: a draft against main. The body opens with Part of #25521 (not Fixes). Clause-②: no and Metadata-protocol: none are line-initial. skip-changeset applies because this is a move. check-governed-merges --pr 26490 reads NOT governed, 1,172 changed lines. None of the four contract faces is touched, so no at-tier review is owed. This is the last cut before the pause (「拆分线已经做了一半的继续完成,然后暂停,等新版本发布后继续」, 2026-10-27T04:17Z); it was in progress when that line was written.

  2. Premise: the claim 6432488188 asked for a seam table re-measured after cut 14, and it is in the body. The backend fault classifier the order priced at about −216 lands as the whole read-exit fault family at −445 (19,794 → 19,349). The six bodies move into sql-driver-read-exit-fault.ts behind ReadExitFaultHost, together with the four module-level declarations only they read:

    • backendStatementFault
    • aggregateBackendFault
    • distinctBackendFault
    • the filter, aggregate and distinct unresolvable-column refusals
  3. Move check (the seat's own): every line removed from sql-driver.ts was compared with the new module after stripping indentation. All 490 non-blank lines are present there except five:

    • Two import names (declareTargetedTable, isMissingTableError) that only the moved bodies read. The module imports them itself.
    • Three declarations that staying code still reads, each gaining export in place: PRE_DDL_QUESTION_SCOPE, unsupportedFilterError and isFilterNode.

    unresolvableColumnNameOf was already exported from sql-driver.ts, and it moves to the module. Its only importer outside the module is a test, which is migrated (see 4). index.ts re-exports sql-driver.js by name only and names none of these declarations, so the published surface does not move. The dev reports the .d.ts hash 0f33bf3f unchanged, with a positive control.

  4. Path readers, migrated in the same PR:

    • sql-driver-compile-refusal-seam.test.ts's INVALID_FILTER builder scan now reads the third file too, so the moved builder stays enumerated.
    • sql-driver-unresolvable-where-column-refusal.test.ts imports unresolvableColumnNameOf from its new home.
  5. Readings at base and head (the dev's): check:adr-anchors 1,876 = 1,876; the driver-sql, turso and wasm suites match test for test (4,060 / 2,526 / 746); the AST proof is OK with a negative control.

  6. Gates: at this stamp every check-run on the head reads success or skipped (path-filtered UI, docs build, dogfood matrix placeholder, opt-in harnesses, skipped changeset check). None reads failed or in progress, and Test Core 1–8, Lint & Repo Gates and TypeScript Type Check are green. Locally the dev reports 53 of 54 derived gates green and test:repo green. dual-build-cjs-loads was not measured locally and is left to CI's full build.

  7. Out of scope (none filed):

    • check:tenant-chokepoint's SCAN_FILES does not list the new module. The moved bodies hold 0 of the 4 tenant predicates and build no query, and widening the list would be a gate change.
    • OWN_STATEMENT_FAULT has no in-file reader left but stays, because sql-driver-raw-execution.ts imports it.
    • Three staying docblocks in sql-driver.ts still {@link unresolvableColumnNameOf}, which now lives in the module. This is comment-only and can go with the restart.
    • The next slice after the pause is the rotation sub-banner (about −118).
  8. Landing: this seat runs pr_ready and then automerge_enable through the relay, confirms MERGED on both readings, and records the landing on [maintainer] refactor(driver-sql): split src/sql-driver.ts (22,096 lines, +2,533 in 30 days) along its SqlDriver seams, one move-only slice per PR #25521. The same act moves the card to pm:on-hold with a Restart-when: naming the next release.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 05:33
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 05:33
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 659f2d9 Oct 27, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-uliqpv branch October 27, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants