Skip to content

refactor(driver-sql): move the cross-field and now-anchored comparison compile out of sql-driver.ts into sql-driver-cross-field-comparison.ts (#25521 cut 13) - #26377

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

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

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 里「跨字段比较」与「相对当前时间比较」({ $field }、{ $now, addDays } 过滤条件)的 SQL 编译代码原样搬进新文件 sql-driver-cross-field-comparison.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 拆分超大文件的第 13 刀;这块在剩余可搬区域里收益最大,sql-driver.ts 减少 470 行。
  • 风险与回滚:纯搬移,无行为变化;三个 SQL 驱动的测试两端逐条一致,公开类型声明哈希不变。回滚即还原本 PR。
  • 先看:sql-driver-cross-field-comparison.ts 的头注释与 host 接口;sql-driver.ts 中四个方法的转交桩;sql-driver-compile-refusal-seam.test.ts 的枚举改为同时读两个文件。
  • 两分钟验证:pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-compile-refusal-seam.test.ts,预期 115 个测试全部通过。
  • 需要你做的:无,席位落地。

What changes

Cut 13 of the sql-driver.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6429655341. The cut opens with a fresh seam table (below), taken on origin/main 77a52cfe8 (after cut 12's merge 8b39b77674), and takes the area that pays best without breaking a series limit: the cross-field and now-anchored comparison compile of the Filter helpers section. It moves unedited into a new seam module, packages/drivers/driver-sql/src/sql-driver-cross-field-comparison.ts, through a new host CrossFieldComparisonHost:

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-5r39ss (the platform assigned it to this card thread, as the claim records). The move is one commit, b1f89c17c, on 77a52cfe8; then origin/main was merged once before opening (e1c1a2b07c), which brought no packages/drivers/** file.

Net line count (the seat's rule). sql-driver.ts goes from 20,492 lines to 20,022: -470.

  • Out: the four bodies, 328 lines (189 + 112 + 9 + 18). Five module-level declarations that only these bodies read, 172 lines with their docblocks plus 5 separator lines: CrossFieldOffset (22), fieldReferenceOffsetOf (16), uncompilableNowAnchorError (22), crossFieldComparisonClass (74) and uncompilableFieldReferenceError (38). And the 6-line spec import crossFieldColumnVerdict, type CrossFieldComparisonClass with its comment, whose last reader (crossFieldComparisonClass) moved; it moved whole into the module.
  • In: the four stub bodies, 37 lines (each is 4 lines, plus 2 per field read and 1 per method read), and a 4-line import of the module with its comment.
  • The new module is 608 lines.

Serial with the file's in-flight PRs. I read the full file list of every open PR twice: at start, before cut 12 merged (22 PRs), and again after it, before pr_create (19 PRs). No open PR touches sql-driver.ts, sql-driver-compile-refusal-seam.test.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.

Seam table, fresh, on 77a52cfe8

  • Size and churn. sql-driver.ts is 20,492 lines. The 30-day column counts git blame lines from commits on or after 2026-09-26T23:48:01Z, 30 days before the base commit. History reaches 2026-09-16. scripts/pm/git-history.mjs count proves 73 commits on the file in 30 days and 31 in the last 7. In all, 3,618 lines (17.7%) are from the last 30 days.
  • Class shape. SqlDriver spans lines 5452-20417 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-12 used, which strips casts, parentheses, ! and satisfies before it reads this. Sections are the class's own banner comments.

|:--|:--|:--|--:|--:|:--|--:|--:|:--|
| 1-5451 | Module preamble | - | 5,451 | 855 | - | - | - | - |
| 5452-7043 | Class head: fields, statics, constructor, capability getters | name … assertDialectEmits | 1,592 | 290 | 8 (1) | 1 | 0 | 18 (18) |
| 7044-7475 | Lifecycle | connect … disconnect | 432 | 155 | 8 (3) | 0 | 0 | 6 (6) |
| 7476-9986 | CRUD — IDataDriver core | find … delete | 2,511 | 736 | 43 (7) | 0 | 12 | 0 (0) |
| 9987-10598 | Bulk & Batch Operations | bulkCreate … count | 612 | 119 | 9 (6) | 0 | 7 | 0 (0) |
| 10599-10695 | Raw Execution | rawStatementFault … noteRuntimeIndexDdl | 97 | 28 | 3 (1) | 0 | 0 | 0 (0) |
| 10696-10788 | Transactions | beginTransaction … rollbackTransaction | 93 | 46 | 7 (5) | 0 | 0 | 0 (0) |
| 10789-11320 | Aggregation | aggregate … unresolvableAggregateColumnRefusal | 532 | 60 | 5 (1) | 0 | 1 | 0 (0) |
| 11321-11608 | Distinct | distinct … unresolvableDistinctColumnRefusal | 288 | 17 | 3 (1) | 0 | 1 | 0 (0) |
| 11609-11724 | Window Functions | findWithWindowFunctions … findWithWindowFunctions | 116 | 4 | 1 (1) | 0 | 1 | 0 (0) |
| 11725-11823 | Query Plan Analysis | explain … analyzeQuery | 99 | 1 | 2 (2) | 0 | 1 | 0 (0) |
| 11824-11882 | Schema Sync (syncSchema / init) | syncSchema … reclaimSpace | 59 | 34 | 3 (3) | 0 | 0 | 0 (0) |
| 11883-13431 | Schema Sync (syncSchema / init) › Data-lifecycle rotation (ADR-0057 P2) | rotationStateByTable … migrateMysqlTimeColumns | 1,549 | 158 | 26 (4) | 0 | 0 | 0 (0) |
| 13432-13688 | Schema Sync (syncSchema / init) › Deferred schema DDL (#3917) | setDeferredDdl … flushDeferredSchemaDdl | 257 | 25 | 6 (4) | 0 | 0 | 0 (0) |
| 13689-14503 | Schema Sync (syncSchema / init) › Managed-schema drift & reconcile (#2186) | dialectName … createNullSafeUniqueIndex | 815 | 237 | 23 (3) | 0 | 0 | 0 (0) |
| 14504-14555 | Schema Introspection | listPhysicalColumns … introspectSchema | 52 | 46 | 2 (2) | 0 | 0 | 0 (0) |
| 14556-14826 | Internal helpers | getKnex … auditMissingTenant | 271 | 73 | 8 (1) | 0 | 1 | 0 (0) |
| 14827-17267 | Internal helpers › Filter helpers | tableNameForBuilder … applyFalseConstant | 2,441 | 438 | 44 (2) | 0 | 0 | 0 (0) |
| 17268-17443 | Internal helpers › Field mapping | mapSortField … mapAggregateFunc | 176 | 2 | 6 (0) | 0 | 0 | 0 (0) |
| 17444-17491 | Internal helpers › Window function builder | buildWindowFunction … buildWindowFunction | 48 | 26 | 1 (0) | 0 | 0 | 0 (0) |
| 17492-19438 | Internal helpers › Column creation helper | nowColumnDefault … applyDeclaredColumnDefault | 1,947 | 92 | 19 (0) | 0 | 0 | 8 (8) |
| 19439-19736 | Internal helpers › Database helpers | sqliteFilename … isJsonField | 298 | 76 | 9 (2) | 0 | 0 | 0 (0) |
| 19737-20213 | Internal helpers › SQLite serialisation | formatInput … formatOutput | 477 | 27 | 2 (0) | 0 | 0 | 0 (0) |
| 20214-20417 | Internal helpers › Introspection internals | introspectColumnOrder … introspectUniqueConstraints | 204 | 73 | 5 (0) | 0 | 0 | 0 (0) |
| 20418-20492 | After the class | - | 75 | 0 | - | - | - | - |

Pricing the areas

Eligibility is as cuts 11-12 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. 150 members are eligible on this base. Each is priced as its stub minus its body; per section, the sum over eligible bodies with a negative price and no sql-driver.ts#… pointer is: Filter helpers -550 (22 bodies), the Rotation sub-banner -195 (7), CRUD -157 (12), every other section -93 or less. Each candidate area is then priced net of the module-level declarations it shares with members that stay (they stay in place, exported; ⛔ no second copy of a helper); the declarations only the moved bodies read move with them.

Area (section) Bodies Bodies net Declarations that move along Disposition
Cross-field and $now comparison (Filter helpers: applyCrossFieldComparison, applyNowAnchorComparison, nowAnchorBaseExpr, crossFieldOffsetExpr) 4 -291 -177, and the spec import (-6, +4 for the module import) Moves (this cut) into a new seam module. -470 measured.
CRUD upsert conflict-target path (assertConflictTargetHonoured, introspectKeyIndexes, assertConflictTargetBackedWithoutClause, upsertConflictRefusal, upsertTenantGuard) 5 -104 -218 (indexCoversConflictTarget, refuseAmbiguousConflictTarget, unbackedConflictTargetCause, ConflictTargetVerdict) Not taken: it pays about -320, less. refuseUnbackedConflictTarget and refuseUpsertConflictOutsideWrittenOrganization stay with upsert, which builds through getBuilder. Still the next candidate.
The rest of the Filter helpers (compileFilters -75, applyNormalizedComparison -60, applyEmptyOperator -28, …) - - - Not priced as an area here: small bodies spread across the section around applyFilterCondition, which an ADR-0139 pointer pins in place.

Why this area

  • It pays the most of any area that moves cleanly. The four bodies are one mechanism, the column-to-column and now-anchored arms of the filter compile, and only applyFilterCondition calls them.
  • What stays, and why.
    • applyFilterCondition, the one caller of both arms, stays with the operator dispatch (ADR-0139 names it by path).
    • declaredFieldsFor, resolveTenantField, mapSortField, remoteColumn and filterColumnExpr are read by members that stay too. The moved bodies reach them through the host.
  • Shared declarations, exported in place (cut 2/11/12 shape). crossFieldComparisonError and nowAnchorPositionError (also thrown by assertCompilableComparand, classifyFilterKey and applyFilterCondition), NowAnchor (also read by nowAnchorOf), fieldReferenceOf, mysqlDatetimeLiteral (also read by storageDatetimeValue), withheldFilterError (the [A of #7929] a spec-declared provenance mark set at both read-scope merge boundaries, so the driver can restore the author-facing cross-field diagnostic without re-disclosing policy #8220 seam every compile refusal goes through), JSON_COLUMN_TYPES and NUMERIC_SCALAR_TYPES. 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 eight leaks.
  • No series limit is touched. None of the four 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 four are protected 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 new module. The two arms name the tenant column only to refuse it as an operand (9 and 8 tenantField references, 0 predicates); disclosed because the new module is outside the gate's SCAN_FILES.
  • Subclasses. TursoDriver and SqliteWasmDriver override none of the four. 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 four, or the five moved declarations, by a sql-driver.ts#… pointer or a sql-driver.ts::… key. Prose mentions (formula's matches-filter.ts, objectql's having-filter.ts, runtime's expected-read-refusal-noise.ts, spec's filter-cross-field-comparison-class.ts, cross-field-conformance-cases.ts) name the functions or SqlDriver.applyCrossFieldComparison, which are all still true. The one reader by path that depends on where they live is sql-driver-compile-refusal-seam.test.ts, migrated below. The five cli generate-*.pin.test.ts files read sql-driver.ts text inside createColumn, fieldHasColumn and the width constants, all unchanged; four of them ran green at head (62 tests), the fifth is in the cli integration tier, which CI runs.

The path reader migrated in this PR

sql-driver-compile-refusal-seam.test.ts enumerates every function that builds an INVALID_FILTER error by parsing sql-driver.ts, and pins the set of withheldFilterError callers equal to its DOORS rows. Two of those callers, uncompilableFieldReferenceError and uncompilableNowAnchorError, moved. The enumeration now parses sql-driver.ts and sql-driver-cross-field-comparison.ts with the same visitor; its three docblock lines say so. Nothing else in the test changed, and the DOORS table is untouched.

  • Head: 115 tests pass.
  • Control: the unmigrated test, run against the moved source (a scratch copy, not committed), fails exactly every builder that goes through the seam is driven by a row below, with the two moved builders missing from the scanned set. So the migration is what keeps the gate from going silently empty.
  • Each DOORS row still asserts its builder by the error's own stack (at uncompilableFieldReferenceError), which the move does not change.

The shape (#23050 shape B, as cuts 1-12 apply it)

  • Each class method keeps its docblock and signature, byte-identical (none was async). Its body is now one host literal plus NAME.call(host, ...), marked [#25521 cut 13], and returns the moved function's own result.
  • Fields go as GETTERS that read the driver at call time (isMysql, isSqlite). Methods go as closures that call the method on the driver at call time (crossFieldOffsetExpr, declaredFieldsFor, filterColumnExpr, mapSortField, nowAnchorBaseExpr, remoteColumn, resolveTenantField). So a subclass override, an instance spy, or a field replaced after construction is still the one the body reaches (probe below).
  • CrossFieldComparisonHost lists exactly those nine members, each typed as the class member's indexed access. There is no cast, and no member becomes public.
  • Module imports. temporalStorageForm (@objectstack/core), the moved spec import, type Knex (knex), and from ./sql-driver.js the eight shared declarations plus type SqlDriver.
  • Comments. The module header says what moved, what stayed and why. The sql-driver.ts import comment names cut 13 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.

ADR anchors

  • The count. check:adr-anchors counts 1877 decision-letter citations at base 77a52cfe8 (a clean detached worktree) and 1877 at head, over 90 anchored files at both ends. Both runs exit 0. After the merge of origin/main it still reads 1877.
  • Why equal. No comment inside the moved bodies, declarations or import cites a decision letter. crossFieldComparisonClass's docblock names ADR-0104 without a letter.
  • No entry needs a new home. The sql-driver.ts entry's one paragraph (ADR-0053) describes formatOutput and presentReadValue, neither of which moved. 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-compile-refusal-seam.test.ts 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 (ea341d74, e5a80af5, d0fed3d6), 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 applyCrossFieldComparison in the head index.d.ts. The result hashes to de5a5d07d80992753bdcb2632346daf7a4e45ce1c7983135555d824c64ae97fc, with a 1-line diff and cmp exit 1.
  • Export lists. CJS require and ESM import of the built dist give the same 59 names at both ends (sha1 05757ce5ae). None of the eight 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 43 at base to 47 at head.

Move-only proof

The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base 77a52cfe8 against head b1f89c17c. It is a scratch script and is not committed.

Moved body Body tokens Comments Parameter tokens less this Return type tokens Body lines
applyCrossFieldComparison 1294 16 34 1 189
applyNowAnchorComparison 835 1 34 1 112
nowAnchorBaseExpr 116 0 8 11 9
crossFieldOffsetExpr 135 1 31 11 18
  • 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.
  • The four stubs. Leading trivia (the docblock), modifiers and the signature are identical to base, the two parameter comments included.
  • The class. It has 350 members at both ends, in the same order, and the 346 unmoved members are byte-identical, trivia included.
  • Module-level declarations. The five that moved are byte-identical in the module, leading docblock included, and gone from sql-driver.ts; so is the moved import with its comment. The eight shared ones differ from base by a leading export and nothing else. At sql-driver.ts's top level, 191 of 206 base statements are byte-identical. The rest are the moved import, the five moved declarations, the eight exports and the class.
  • Result. PROOF OK, exit 0.
  • Negative control. I changed this.nowAnchorBaseExpr(anchor.at, targetClass) to this.nowAnchorBaseExpr(anchor.at, 'date') in a module copy (1 hit, checked on disk). The proof then reports applyNowAnchorComparison with its body tokens 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 77a52cfe8 against head b1f89c17c. 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 and prints 68 readings:
    1. every cross-field arm ($eq, $ne, an ordering, addDays literal, addDays column, addDays with $ne) in both where and orWhere, and four now-anchored comparisons, each with the compiled SQL and bindings, and with instance spies on all seven host methods showing the calls in order;
    2. fourteen refusals (dotted path, unresolved or undeclared table, undeclared field, cross-class, JSON class, bad addDays, addDays on a number or from a text column, the unreachable operator arms, an invalid anchor, an anchor with no offset, an anchor on a number), each with code INVALID_FILTER, status 400, message head and tail, and the throwing frame (unsupportedFilterError at both ends);
    3. a SUBCLASS that overrides crossFieldOffsetExpr, nowAnchorBaseExpr and resolveTenantField, each reached (the override's SQL appears in the statement, and its tenant column is refused as an offset);
    4. isSqlite and isMysql shadowed on the instance after construction: the MySQL and PostgreSQL spellings of both operand builders, then SQLite's again;
    5. nine find() calls end to end on SQLite, rows answered, including $not over an addDays comparison and two refusals through find();
    6. bad arguments on each of the four methods.
  • Probe result. 64 of the 68 readings are identical. The other 4 are the bad-argument rows: each throws the same TypeError with the same message, and only the top stack frame's receiver name differs, _SqlDriver.applyCrossFieldComparison at base and Object.applyCrossFieldComparison at head. 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. The repo projects of types (11), core (83), objectql (5) and rest (280 passed, 18 skipped) pass locally after building rest's closure. Runtime's 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 59 commands for the 3 changed paths (merge base 77a52cfe8). On b1f89c17c, 58 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: 59 derived families accounted for, 58 run, 1 NOT-MEASURED (derived from the recorded exit 3), exit 0. After the merge the derived list is the same 59 commands.
  • Card-named gates, read at base (a clean detached worktree at 77a52cfe8) and at head, all exit 0:
Gate Base Head
check:adr-anchors 90 files, 1877 decision-letter citations 90 files, 1877
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 3314 files / 542 classes / 7 positions 3315 files (the new module) / 542 / 7
check:system-context-census 153 sites / 132 symbols same
check-tenant-audit-census 244 sites same
check:logger-receiver-detach 0 detaches, 3756 files 0 detaches, 3757 files (the new module)
  • check:nul-bytes passes. The pre-push check:commit-card-trailers passed on push.

Acceptance notes

  • check:tenant-chokepoint's D8 rule does not scan the new module. The two arms read the tenant column only to refuse it as an operand, and hold 0 of the file's 4 tenant predicates (measured with the gate's own nullArmsOf). The series limit names getBuilder areas only, and none of the four builds a query. Noted, not filed: widening SCAN_FILES would be a gate change.
  • Prose that names a moved function by owner. cross-field-conformance-cases.ts, matches-filter.ts, having-filter.ts and filter-cross-field-comparison-class.ts name SqlDriver.applyCrossFieldComparison or "crossFieldComparisonClass in @objectstack/driver-sql". Each is still true: the method is still a member and the function is still in the package.
  • Next slice, priced on this base. The CRUD upsert conflict-target path pays about -320 (five bodies -104, four declarations that move along -218). Two refusal builders it reads stay with upsert, exported in place.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01DC6m1WLgsQ2NJA2W8mZBQi

…n compile out of sql-driver.ts into a seam module

The bodies of applyCrossFieldComparison, applyNowAnchorComparison,
nowAnchorBaseExpr and crossFieldOffsetExpr move unedited into the new
sql-driver-cross-field-comparison.ts, reached through
CrossFieldComparisonHost; the class keeps every method as a stub. Five
module-level declarations only those bodies read move with them, with the
spec import crossFieldComparisonClass delegates to; eight shared ones gain
export in place. The compile-refusal seam test's enumeration parses both
files, so the two moved refusal builders are still counted.

Claude-Session: https://claude.ai/code/session_01DC6m1WLgsQ2NJA2W8mZBQi
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/automation/flows.mdx (via addDays (literal, a string literal in uncompilableNowAnchorError))
  • content/docs/data-modeling/external-datasources.mdx (via remoteColumn (symbol, a field of interface CrossFieldComparisonHost), remoteColumn (literal, a string literal in applyCrossFieldComparison; a string literal in applyNowAnchorComparison; a string literal in remoteColumn))
  • content/docs/data-modeling/fields.mdx (via addDays (literal, a string literal in uncompilableNowAnchorError))
  • content/docs/data-modeling/formulas.mdx (via addDays (literal, a string literal in uncompilableNowAnchorError))
  • content/docs/protocol/objectql/query-syntax.mdx (via addDays (literal, a string literal in uncompilableNowAnchorError))

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

  • content/docs/releases/v16.mdx (via addDays (literal, a string literal in uncompilableNowAnchorError))

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
  • 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 61c079862ac726a1e16fcd73a04122567f475c4a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ded50458b0ec319e3833e25d665d7c8b3f8b9809 — the merge of head e1c1a2b07c5b26b81a258690010f1d656628d30c into base 61c079862ac726a1e16fcd73a04122567f475c4a, 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 ded50458b0ec319e3833e25d665d7c8b3f8b9809 && git checkout ded50458b0ec319e3833e25d665d7c8b3f8b9809
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 61c079862ac726a1e16fcd73a04122567f475c4a e1c1a2b07c5b26b81a258690010f1d656628d30c && git checkout -B drift-repro 61c079862ac726a1e16fcd73a04122567f475c4a && git merge --no-ff e1c1a2b07c5b26b81a258690010f1d656628d30c

node scripts/docs-audit/affected-docs.mjs --json 61c079862ac726a1e16fcd73a04122567f475c4a

⚠️ 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 61c079862ac726a1e16fcd73a04122567f475c4a → 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-27T01:24Z · head e1c1a2b07c · #25521 cut 13

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; the card stays open for later cuts). Clause-②: no and Metadata-protocol: none are line-initial. skip-changeset applies: the change is move-only. check-governed-merges --pr 26377: NOT governed, 1,197 changed lines.

  2. Premise measured: the claim 6429655341 asked for a fresh seam table after cut 12, and it is in the PR body. The pick is the first of the two areas cut 12 priced: the Filter helpers' cross-field and $now comparison cluster. applyCrossFieldComparison, applyNowAnchorComparison and the operand builders nowAnchorBaseExpr and crossFieldOffsetExpr move with the module-local helpers only they use into the new sql-driver-cross-field-comparison.ts through CrossFieldComparisonHost. Each method stays on the class as a delegate.

    The seat compared every line removed from sql-driver.ts with the module after stripping indentation. All 509 non-blank lines are present there except eight, each a declaration that members staying in the class still read and that gains export in place (cut 2's shape): JSON_COLUMN_TYPES, NUMERIC_SCALAR_TYPES, mysqlDatetimeLiteral, withheldFilterError, fieldReferenceOf, NowAnchor, nowAnchorPositionError and crossFieldComparisonError. index.ts re-exports sql-driver.js by name only, and none of the eight is among those names, so none reaches the published surface; the dev's public .d.ts is identical at base and head with a positive control, exports 59 of 59. Nothing else was added to sql-driver.ts beyond the module import and the delegate bodies. sql-driver.ts goes from 20,492 to 20,022 (-470).

  3. Path reader, migrated in the same PR: sql-driver-compile-refusal-seam.test.ts's enumeration of the functions that build an INVALID_FILTER error now reads both sql-driver.ts and the new module, so the two builders that moved stay enumerated and the scan does not go silently short.

  4. Readings at base and head (the dev's): check:adr-anchors 1,877 = 1,877; the tenant-chokepoint D8 predicates 4 = 4; the durability seams 48 = 48; the driver-sql (4,060), turso (2,526) and sqlite-wasm (746) suites with 0 lost or changed; the AST proof passing and its negative control failing.

  5. Gates: at this stamp the head's 52 check-runs read 36 success and 16 skipped (path-filtered UI, docs-build, dogfood matrix placeholders, opt-in harnesses and the skipped changeset check), with none failing or in progress, Test Core 1–8 included; mergeable, clean. Locally the dev reports 58 of 59 derived gates exit 0, with dual-build-cjs-loads left to CI's full build, which is green.

  6. Out of scope: none filed. The dev notes that check:tenant-chokepoint's SCAN_FILES does not list the new module. The two moved arms read the tenant column only to refuse it as an operand and hold none of the four D8 predicates (4 = 4 at both ends), and they build no query through getBuilder, so the series limit on getBuilder areas does not apply; widening SCAN_FILES would be a gate change this card rules out.

  7. 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.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 01:26
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 01:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit abb9046 Oct 27, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-5r39ss branch October 27, 2026 01:49
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