Skip to content

refactor(driver-sql): move the schema-drift pipeline (detect, reconcile, apply) out of sql-driver.ts into sql-driver-schema-sync.ts (#25521 cut 10) - #26287

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5jymjn
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5jymjn

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

Cut 10 of the sql-driver.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6423425118. The cut opens with a fresh seam table (below) and takes the area it prices best that breaks no series limit. The bodies of six SqlDriver methods move unedited into the existing seam module packages/drivers/driver-sql/src/sql-driver-schema-sync.ts, through its existing host SchemaSyncHost. Together they are the managed-schema drift pipeline, the code that sits on top of the reconcilers and the index machinery cuts 6, 8 and 9 already moved there:

  • detectTableDrift, which diffs one table's columns against its physical schema;
  • detectTableIndexDrift, which diffs that table's indexes and runs the ADR-0120 pre-flight on the result;
  • detectManagedDrift (public), which runs the table diff over every managed table;
  • planMediaColumnMove (public), the read-only plan of the ADR-0104 media column move;
  • reconcileAndWarnDrift, the boot-time reconcile-or-warn;
  • applyMigrationEntries (public), which applies a set of drift entries.

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 and no new gate. Branch claude/project-thread-5jymjn (the platform assigned it to this card thread, as the claim records). One commit, 5fba834b4, on origin/main 48fba2b178.

Net line count (the seat's rule). sql-driver.ts goes from 20,990 lines to 20,739: -251.

  • Out: the six bodies, 314 lines (52 + 32 + 21 + 75 + 57 + 77), and 15 import names whose last reader was a moved body (one line each).
  • In: the six stub bodies, 76 lines (12 + 10 + 11 + 13 + 16 + 14, each 4 + 2 per field or getter read + 1 per method read), and 2 import-comment lines.
  • Priced before the move at -238 for the bodies alone; the import names are the rest.
  • The module grows from 777 to 1,154 lines.

Serial with the file's in-flight PRs. I read the full file list of every open PR at start (37 PRs) and again before pr_create (35 PRs, 17 of them new). No open PR touches sql-driver-schema-sync.ts, turso-driver.ts, the drift section of sql-driver.ts or the driver-sql anchor entries.

Seam table, fresh, on 48fba2b178

origin/main is at 48fba2b178, after cut 9 (ab112ea8d5).

  • Size and churn. sql-driver.ts is 20,990 lines. 30-day lines are git blame lines from commits on or after 2026-09-26T17:15:20Z, 30 days before the base commit. I deepened history to 2026-09-18 first, and scripts/pm/git-history.mjs count proves 69 commits on the file in that window (27 in the last 7 days). In all, 3,202 lines (15.3%) are from the last 30 days.
  • Class shape. SqlDriver spans lines 5513-20915 with 348 members: 241 instance methods (49 public), 13 accessors and 32 statics, all 32 of them private or protected. 24 of the instance methods are already stubs from cuts 1-9.
  • Inferred return types. One public member has one: the supports getter, in the class head.
  • How it was measured. A TypeScript compiler AST script, which strips casts, parentheses, ! and satisfies before it reads this. Sections are the class's own banner comments (// === blocks and // ── sub-banners), as cut 9 cut them; cut 9's table also folds the docblock heading "The sequence semantics a second face shares" into CRUD, and so does this one. "Methods" counts instance method declarations, without accessors.
Lines Section First … last member Lines 30-day Methods (public) Inferred public getBuilder sites Statics (private/protected)
1-5512 Module preamble - 5,512 754 - - - -
5513-7104 Class head: fields, statics, constructor, capability getters name … assertDialectEmits 1,592 290 8 (1) 1 0 18 (18)
7105-7536 Lifecycle connect … disconnect 432 155 8 (3) 0 0 6 (6)
7537-10241 CRUD — IDataDriver core find … delete 2,705 739 43 (7) 0 12 0 (0)
10242-10853 Bulk & Batch Operations bulkCreate … count 612 119 9 (6) 0 7 0 (0)
10854-10950 Raw Execution rawStatementFault … noteRuntimeIndexDdl 97 28 3 (1) 0 0 0 (0)
10951-11043 Transactions beginTransaction … rollbackTransaction 93 46 7 (5) 0 0 0 (0)
11044-11575 Aggregation aggregate … unresolvableAggregateColumnRefusal 532 60 5 (1) 0 1 0 (0)
11576-11863 Distinct distinct … unresolvableDistinctColumnRefusal 288 17 3 (1) 0 1 0 (0)
11864-11979 Window Functions findWithWindowFunctions … findWithWindowFunctions 116 4 1 (1) 0 1 0 (0)
11980-12078 Query Plan Analysis explain … analyzeQuery 99 1 2 (2) 0 1 0 (0)
12079-12137 Schema Sync (syncSchema / init) syncSchema … reclaimSpace 59 34 3 (3) 0 0 0 (0)
12138-13879 Schema Sync (syncSchema / init) › Data-lifecycle rotation (ADR-0057 P2) rotationStateByTable … migrateMysqlTimeColumns 1,742 72 26 (4) 0 0 0 (0)
13880-14136 Schema Sync (syncSchema / init) › Deferred schema DDL (#3917) setDeferredDdl … flushDeferredSchemaDdl 257 25 6 (4) 0 0 0 (0)
14137-15189 Schema Sync (syncSchema / init) › Managed-schema drift & reconcile (#2186) dialectName … createNullSafeUniqueIndex 1,053 193 23 (3) 0 0 0 (0)
15190-15241 Schema Introspection listPhysicalColumns … introspectSchema 52 46 2 (2) 0 0 0 (0)
15242-15512 Internal helpers getKnex … auditMissingTenant 271 73 8 (1) 0 1 0 (0)
15513-17765 Internal helpers › Filter helpers tableNameForBuilder … applyFalseConstant 2,253 250 42 (2) 0 0 0 (0)
17766-17941 Internal helpers › Field mapping mapSortField … mapAggregateFunc 176 2 6 (0) 0 0 0 (0)
17942-17989 Internal helpers › Window function builder buildWindowFunction … buildWindowFunction 48 26 1 (0) 0 0 0 (0)
17990-19936 Internal helpers › Column creation helper nowColumnDefault … applyDeclaredColumnDefault 1,947 92 19 (0) 0 0 8 (8)
19937-20234 Internal helpers › Database helpers sqliteFilename … isJsonField 298 76 9 (2) 0 0 0 (0)
20235-20711 Internal helpers › SQLite serialisation formatInput … formatOutput 477 27 2 (0) 0 0 0 (0)
20712-20915 Internal helpers › Introspection internals introspectColumnOrder … introspectUniqueConstraints 204 73 5 (0) 0 0 0 (0)
20916-20990 After the class - 75 0 - - - -

Pricing the eligible methods

A method is eligible to move when it meets every condition below. 168 of the 241 instance methods do (24 are already stubs, 19 build through getBuilder, 17 read a private static, 12 write to this, 3 have a this.FIELD.delete/insert/update( site, and one is insertOnlyUpsertColumns).

  • Its return type is annotated, or it is not public.
  • It has no getBuilder site, no this.FIELD.delete/insert/update( site, no private or protected static read, no super read, no bare this pass and no write to this.
  • It is not insertOnlyUpsertColumns.

The net-line rule. Each candidate is priced as its stub minus its body. A stub costs 4 lines, plus 2 for each field or getter read and 1 for each method read.

The highest-priced candidates, and why each one stays or moves:

Method (section) Net Disposition
createColumn (Column creation helper) -463 Stays. ADR-0113 cites sql-driver.ts#createColumn, and five packages/cli generate-*.pin.test.ts files read this file's text.
applyFilterCondition (Filter helpers) -357 Stays. ADR-0139 cites sql-driver.ts#applyFilterCondition.
formatOutput (SQLite serialisation) -236 Stays. ADR-0104 cites it, and the sql-driver.ts anchor entry's ADR-0053 paragraph describes it.
initObjects (Rotation) -195 Stays. packages/spec/liveness/object.json cites it.
formatInput (SQLite serialisation) -183 Stays. ADR-0053 and ADR-0104 cite it.
applyCrossFieldComparison (Filter helpers) -179 Not taken. Open PR #26261 is editing the cross-field area of Filter helpers right now, and the body reads four module-local helpers (crossFieldComparisonClass, crossFieldComparisonError, fieldReferenceOffsetOf, uncompilableFieldReferenceError) that other class members also call, so it would need exports or a helper move.
assertConflictTargetHonoured (CRUD) -89 Not taken: it reads five module-local declarations of the upsert conflict-target refusal.
backendStatementFault (Bulk & Batch) -75 Not taken: a single method, and no seam module holds the fault doors yet.
compileFilters (Filter helpers) -75 Not taken: Filter helpers (see #26261 above).
getNextSequenceValue (CRUD) -66 Not taken here; proposed below with the rest of the sequence area.
applyMigrationEntries (drift) -63 Moves (this cut).
planMediaColumnMove (drift) -62 Moves (this cut).
reconcileAndWarnDrift (drift) -41 Moves (this cut).
detectTableDrift (drift) -40 Moves (this cut).
detectTableIndexDrift (drift) -22 Moves (this cut).
detectManagedDrift (drift) -10 Moves (this cut).

Priced by area (every eligible body with a negative price and no sql-driver.ts#… pointer, summed per section): Filter helpers -448 (20 bodies), Rotation -388 (15), CRUD -351 (18), Managed-schema drift -275 (10), and every other section -93 or less.

Why this area

  • It pays the most of any area that moves cleanly. The three sections priced above it each fail on the card's own terms:
  • The six bodies read no module-local declaration of sql-driver.ts. Every declaration they read is imported from the module that declares it (schema-drift.ts, media-column-move.ts, @objectstack/spec/system) or is the exported type IntrospectedColumn. No export was added anywhere, and no helper moved.
  • They belong in an existing module and host. sql-driver-schema-sync.ts already holds what this pipeline calls: applyDriftOpInPlace and rebuildSqliteTablePatched (cut 6), applyIndexDriftOp (cut 8), applyNullSafeUniquePreflight (cut 9). SchemaSyncHost already listed 8 of the 26 members these bodies read.
  • No series limit is touched:
    • none of the six builds through getBuilder;
    • none reads a static;
    • none has a this.FIELD.delete/insert/update( site (this.driftWarned.add( is a Set write, read through the host getter, and the tenant-audit census reads the same 243 sites at both ends);
    • none uses super, a bare this or a cast-wrapped this, and none writes to this;
    • the three public ones (detectManagedDrift, planMediaColumnMove, applyMigrationEntries) have annotated return types; the other three are protected.
  • Subclasses. TursoDriver overrides detectManagedDrift, applyMigrationEntries and planMediaColumnMove, and each calls super on its local path. The stubs stay on the class with the same signatures, so super still reaches them, and the moved bodies reach every method through a late-bound closure on the instance, so Turso's introspectColumns, introspectIndexes and applyMigrationEntries overrides are still the ones that run. One driver-sql test (sql-driver-11722-sqlite-rebuild-applied-honesty.test.ts) overrides detectTableDrift in a subclass; it passes at both ends, and the probe below covers the same shape.
  • Path readers, screened. No script, test, doc or ADR names any of the six by a sql-driver.ts#… path, and no test or gate slices their text out of sql-driver.ts (the files that read sql-driver.ts by path are the five cli generate-* pins, logger-receiver-detach.test.ts, sql-driver-compile-refusal-seam.test.ts and the gates in the table below; none of them reads these bodies). See Acceptance notes for prose that names them.

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

  • Each class method keeps its docblock and signature, byte-identical less async. Its body is now one host literal plus NAME.call(host, ...), marked [#25521 cut 10], and it returns the moved function's own promise.
  • Fields and getters go as GETTERS that read the driver at call time: autoMigrate, dialectName, driftWarned, fileColumnsMoved, isSqlite, knex, logger, managedObjectFields, managedObjectIndexes, mediaFields, runtimeCreatedIndexes and schemaMode. Methods go as closures that call the method on the driver at call time. So a subclass override, an instance spy, or a field replaced after construction is still the one the body reaches (see the probe below). this.logger.info(...) and this.logger.warn(...) still call through the logger itself, as the [finding] driver-sql has 10 inline (this.logger.error ?? this.logger.warn)(…) detach-then-call sites — the shape #12773 measured crashing, in a file that already contains the correct spelling #12792 comment in reconcileAndWarnDrift requires.
  • Each moved function types its this as the Pick of SchemaSyncHost over exactly the members it reads. Each stub annotates its literal as the ThisParameterType of the moved function. There is no cast, and no member becomes public.
  • SchemaSyncHost gains 18 members, each typed as the class member's indexed access, in the interface's alphabetical order: applyDriftOpInPlace, applyMigrationEntries, applyNullSafeUniquePreflight, assertSchemaMutable, autoMigrate, detectTableDrift, detectTableIndexDrift, driftWarned, fileColumnsMoved, introspectIndexes, isProductionEnv, isSqlite, isTenantKeyCertified, mediaFields, rebuildSqliteTablePatched, runtimeCreatedIndexes, schemaMode and varcharColumnChars.
  • Module-level declarations.
    • The module now imports from schema-drift.ts carriesNullSafeTenantKeyPart, ceremonyOwnedDropFinding, diffManagedIndexes, diffManagedTable, diffUnbuildableIndexes, driftKey, expectedIndexes, indexedKeyColumns, isCeremonyOwnedColumnDrop, legacyUniqueReplacements and the type PhysicalColumn; from media-column-move.ts isJsonColumnType, mediaColumnMoveDialect, mediaColumnMovePlan and the three MediaColumnMove… types; StorageNameMapping from @objectstack/spec/system; and the type IntrospectedColumn from sql-driver.ts (a type-only import, as the module already does for SqlDriver).
    • sql-driver.ts drops the 15 of those names it no longer reads (all but diffManagedIndexes, indexedKeyColumns, StorageNameMapping, IntrospectedColumn and MediaColumnMoveScan, which other code there still reads). isIndexDriftOp is one of the 15, because applyMigrationEntries was its last reader there.
    • index.ts re-exports nothing from the module, so the published surface does not move.
  • Comments. The module header gains a [#25521 cut 10] paragraph that cites no decision letter. The sql-driver.ts import comment names cut 10 in 2 lines.
  • 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 line in the moved bodies starts inside template-literal text.

ADR anchors

check:adr-anchors counts decision-letter citations on the anchored surface: each anchor's invariant, plus the full text of each anchored file.

  • Both files are anchored. sql-driver.ts has its ADR-0053 entry, and sql-driver-schema-sync.ts has the ADR-0120 entry cut 8 created.
  • The citations move between anchored files. The moved bodies carry their ADR-0131 D1 and ADR-0131 D10 citations from one anchored file to the other, and the new header paragraph cites no letter.
  • The count. It is 1881 decision-letter citations at base 48fba2b178 and at head 5fba834b4, over 90 anchored files at both ends. Both runs exit 0.
  • No entry needs a new home. Neither entry's paragraph describes a moved body: the ADR-0053 paragraph describes formatOutput and presentReadValue, and the ADR-0120 D4 paragraph describes the tightening ceremony that cuts 8 and 9 moved.

The published .d.ts does not change

This is measured under the series bar (ruling 6348891160, item 1).

  • How each end was built. I built each end with pnpm turbo run build --filter=@objectstack/driver-sql --force, against the same dependency tree. The base leg writes the base text of the two source files into this worktree after the commit, under an EXIT/INT/TERM trap that restores HEAD. The restore is proved by the HEAD blob hashes (d4d08264, 90b63682), 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: 5028e040e1501b0bd72b91fafcf4e1cb372393c59baa5bab8b936b240dd2d3f5 (cut 9's hash). cmp finds them identical.
  • Positive control. I dropped protected from detectTableDrift in the head index.d.ts. The result hashes to a9d58e424308f85ac7429096c80df2554b41abe53d67410e030799a463072be5, with a 1-line diff and cmp exit 1.
  • Export lists. CJS require and ESM import of the built dist give 56 / 56 names at both ends, identical.
  • The head dist carries the move. .call(host sites in index.mjs go from 24 at base to 30 at head.

Move-only proof

The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base 48fba2b178 against head 5fba834b4. It is a scratch script and is not committed.

Moved body Body tokens Comments Parameter tokens less this Return type tokens Body lines
detectTableDrift 231 16 17 6 52
detectTableIndexDrift 149 15 27 6 32
detectManagedDrift 239 0 28 6 21
planMediaColumnMove 412 4 0 4 75
reconcileAndWarnDrift 351 15 17 4 57
applyMigrationEntries 470 18 17 16 77
  • Each moved body. In every row, 0 tokens differ and every comment is identical. async is on both ends, and there are no type parameters. Each body line is the base line less two leading spaces.
  • The six stubs. Leading trivia (the docblock), modifiers and the signature are identical to base, less async.
  • The class. It has 348 members at both ends, in the same order, and the 342 unmoved members are byte-identical, trivia included.
  • sql-driver.ts top level. It has 206 statements at both ends. The only base statements that differ are the schema-drift.js, media-column-move.js and sql-driver-schema-sync.js imports, and the class.
  • sql-driver-schema-sync.ts. It goes from 12 to 20 statements. The base statements that differ are the first import (which carries the module header), the schema-drift.js and sql-driver.js imports, and SchemaSyncHost. The new statements are the @objectstack/spec/system and media-column-move.js imports and the six functions.
  • Result. PROOF OK, exit 0.
  • Negative control. I changed could not introspect to could not intr0spect in a module copy. The proof then reports reconcileAndWarnDrift with its body tokens and lines differing, prints PROOF FAILED, and exits 1.

Tests

Each end ran under one os-verify-lock hold, after building that end's driver-sql dist. I compared the vitest JSON per test name and status, base 48fba2b178 against head 5fba834b4. Every part's exit code was captured on its own (build, three suites: all 0 at both ends).

Suite Base Head Lost / changed / added
@objectstack/driver-sql 4010 (3796 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 704 (704 passed; 39 files) same 0 / 0 / 0
  • Type check and lint. pnpm exec tsc --noEmit -p . in driver-sql exits 0. pnpm exec eslint --no-inline-config on the two touched files exits 0, with 0 problems.
  • Late-binding probe. This is a scratch script, not committed. It runs node against each end's built dist on in-memory SQLite. Its 27 readings print identical output at base and head (diff is empty). They cover:
    1. detectManagedDrift reaching a SUBCLASS override of detectTableDrift (from the walk and with explicit objects), and managedObjectFields replaced on the instance after construction;
    2. detectTableDrift reaching instance spies on introspectColumns and detectTableIndexDrift, with the physical column set and the NOT NULL witness it passes;
    3. detectTableIndexDrift reaching instance spies on introspectIndexes and applyNullSafeUniquePreflight (twice, around a runtimeCreatedIndexes entry added on the instance; the index result is empty both times at both ends, so that reading proves the calls, not the ledger read);
    4. reconcileAndWarnDrift with autoMigrate: 'safe' on a managed schema, through a logger and a driftWarned replaced after construction: the warn-once lines, the production arm through an instance spy on isProductionEnv, and the introspection-failure warning through an instance override of detectTableDrift;
    5. applyMigrationEntries on SQLite: a destructive entry skipped, the column entries through an instance spy on rebuildSqliteTablePatched, an index entry through an instance override of applyIndexDriftOp, and the empty set;
    6. planMediaColumnMove: a planned unquote, mediaFields replaced after construction (an absent column refused), and a subclass dialectName getter answering the unsupported-dialect refusal;
    7. detectTableDrift with fileColumnsMoved flipped on the instance (an empty result both ways at both ends on SQLite, so it proves only that the read does not throw);
    8. bad arguments to each moved method answering a rejection (or the same resolution base gives), never a synchronous throw.
  • Not run. No ablation, because no tested behaviour changed. test:repo was not run locally: the diff touches no repo-project test, and CI's Test Core runs it.

Gates

All local runs are on head 5fba834b4.

  • Derived gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 55 commands from the change set against merge base 48fba2b17 (2 paths, 802 changed lines). I captured each exit code before any pipe.
    • 52 exit 0 on the first run.
    • check-plugin-teardown-shape.mjs --self-test exited 3 on the shallow clone, then 0 after git fetch origin 621a4876 (its positive control).
    • check:lean-entry-closure exited 3 with no objectql dist, then 0 after pnpm turbo run build --filter=@objectstack/objectql under the lock.
    • check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET: it needs every package's dist. It is NOT MEASURED locally, and CI runs it.
  • Reconciliation. --ran reports "55 derived famil(ies) accounted for — 54 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)", exit 0.
  • Lint. driver-sql has no lint script, so I ran the root lint's command scoped to the package: pnpm exec eslint --no-inline-config packages/drivers/driver-sql, exit 0.
  • Card-named gates. I read each at base in a clean detached worktree at 48fba2b178 and at head 5fba834b4. All exit 0 at both ends, and each prints the same reading at both ends except the one noted.
Gate Base Head Note
check:adr-anchors 90 anchored files, 1881 citations same
check:adr-symbol-anchors 2664 anchors resolve same
check:tenant-chokepoint 24 getBuilder() bindings across 3 files; rule 2 clean same See the first acceptance note.
check:logger-receiver-detach 0 detaches, 3740 non-test TS files 0 detaches, 3741 files The head has one more walked file: the base worktree lacks the gitignored generated packages/spec/src/migrations/registry.ts.
check:driver-conformance 55 covered, 0 DEBT, 0 exempt same
check:keyed-text-bounds 112 object files, 238 index entries same
check:object-def-param-keys 3300 source files, 540 classes, 7 override positions same It reads sql-driver.ts by path.
check:system-context-census 151 read sites, 144 symbols cited same
check-tenant-audit-census 243 write call sites certified same
  • Not owed. check-platform-checklist --changed-from is not owed: no exported symbol was renamed or deleted. Pre-push check:commit-card-trailers and check:nul-bytes pass.
  • origin/main moved while this ran. It moved 19 commits, to 2409c9c8d7. In packages/drivers/**, packages/types/src, packages/spec/src/system, scripts/adr-anchors/**, check-adr-anchors.mjs and check-tenant-chokepoint.mjs, only packages/types/src/node.test.ts changed (one line). git merge-tree against it is clean. I did not merge, because CI tests the merge ref.

Acceptance notes

  • check:tenant-chokepoint rule 2 visits a little less. Rule 2 (the D8 null-arm rule) scans class members of the three driver files, so it no longer visits what moved: the resolveTenantField bindings in detectTableDrift and detectTableIndexDrift. Neither says IS NULL against a tenant column, and neither is a where arm. The verdict is unchanged, and the gate is not empty (24 bindings, 3 files). This is the same disclosure cuts 5-9 made, and the card already carries the SCAN_FILES precondition.
  • logger-receiver-detach.test.ts ③ reads sql-driver.ts only. Its structural pin no longer sees reconcileAndWarnDrift's receiver-kept log calls, which now live in the module. The repo-wide gate check:logger-receiver-detach walks every non-test TS file, the module included, and reads 0 detaches at head. The runtime case in the same test file (reconcileAndWarnDrift through a class-based logger) still runs and passes.
  • Prose that names the moved bodies still holds. docs/qa/platform-checklist/areas/cli.json cites sql-driver.ts#SqlDriverConfig with a parenthetical describing reconcileAndWarnDrift's gate and applyMigrationEntries' allowDestructive: false call, and one verify line says "(sql-driver.ts)". The anchor symbol SqlDriverConfig did not move, and both methods are still on the class, so the symbol anchor resolves (check-platform-checklist judges the symbol, not the parenthetical); the behaviour described now runs from the module. schema-drift.ts, turso-driver.ts and several tests name SqlDriver.detectTableDrift and the others in prose; each is still a SqlDriver method with the same docblock.
  • Two comments in sql-driver.ts now point across files. The docblock of driftWarned says "keyed by {@link driftKey}", and the media-column-move.js import comment says the column step's statements are "Built here". driftKey and the statement builders are now imported by the module rather than by sql-driver.ts. I left both comments unedited, since the cut is move-only.
  • Proposed next slice: the sequence area of CRUD (getNextSequenceValue -66, fillAutoNumberFields -52, issueAutonumbers -40, maxAutonumberCounter -19, resyncSequenceToDataMax -13 and their small helpers, about -200). It would need a new seam module. Before pricing it at dispatch, screen SEQUENCES_TABLE (module-local, read by other members; the class already has a sequencesTableName getter), and TursoDriver's overrides of getNextSequenceValue, autoNumberValueExists and resyncSequenceToDataMax, which the "stated once" banner says a second face must call, never copy.

skip-changeset: the change is move-only, so nothing a published package ships changes. The .d.ts is byte-identical, and the emitted JS runs the same bodies through the host.


Generated by Claude Code

…driver-schema-sync.ts

Cut 10 of the sql-driver.ts split, move-only. The bodies of
detectTableDrift, detectTableIndexDrift, detectManagedDrift,
planMediaColumnMove, reconcileAndWarnDrift and applyMigrationEntries move
unedited into the existing seam module, through SchemaSyncHost. The class
keeps every method, its docblock and its signature (less `async`); each
body is now one host literal plus a `.call`.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/data-modeling/drivers.mdx (via autoMigrate (symbol, a field of interface SchemaSyncHost), schemaMode (symbol, a field of interface SchemaSyncHost), autoMigrate (literal, a string literal in autoMigrate; a string literal in reconcileAndWarnDrift), schemaMode (literal, a string literal in reconcileAndWarnDrift; a string literal in schemaMode))
  • content/docs/data-modeling/external-datasources.mdx (via schemaMode (symbol, a field of interface SchemaSyncHost), schemaMode (literal, a string literal in reconcileAndWarnDrift; a string literal in schemaMode))
  • content/docs/deployment/cli.mdx (via autoMigrate (symbol, a field of interface SchemaSyncHost), autoMigrate (literal, a string literal in autoMigrate; a string literal in reconcileAndWarnDrift))

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

  • content/docs/releases/v17/17-6.mdx (via schemaMode (symbol, a field of interface SchemaSyncHost), schemaMode (literal, a string literal in reconcileAndWarnDrift; a string literal in schemaMode))
  • content/docs/releases/v18/18-1.mdx (via schemaMode (symbol, a field of interface SchemaSyncHost), schemaMode (literal, a string literal in reconcileAndWarnDrift; a string literal in schemaMode))

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 54 of 206 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 2409c9c8d75c45e118a0706e4b52f4265810aaf7 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 2409c9c8d75c45e118a0706e4b52f4265810aaf7

⚠️ 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 2409c9c8d75c45e118a0706e4b52f4265810aaf7 → 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-26T19:22Z · head 5fba834b47288c291d287b3623899dc1fa059bad · #25521 cut 10

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 26287: NOT governed, 802 changed lines.
  2. Premise measured: the claim 6423425118 asked for a fresh seam table after cut 9; it is in the PR body. The pick is the managed-schema drift pipeline: detectTableDrift, detectTableIndexDrift, detectManagedDrift, planMediaColumnMove, reconcileAndWarnDrift and applyMigrationEntries move into the existing sql-driver-schema-sync.ts through SchemaSyncHost (18 members added). The seat compared every line removed from sql-driver.ts with the module after stripping indentation: all 336 are present there except the import lines (each moved name is now imported by the module from schema-drift.js or media-column-move.js) and the six signatures, which become this-typed module functions. Each method stays on the class as a delegate through this, so an override or a spy is still the one the moved body reaches; the public detectManagedDrift, planMediaColumnMove and applyMigrationEntries keep their signatures. sql-driver.ts goes 20,990 → 20,739. The dev reports .d.ts identical with a positive control, the AST proof OK with a negative control, the three driver suites at 0 lost, changed or added, and check:adr-anchors 1,881 = 1,881.
  3. Gates: at this stamp the head's 52 check-runs read 36 success and 16 skipped (path-filtered UI, docs-build, dogfood matrix placeholders and opt-in harnesses, plus the duplicate-event twins), none failing or in progress; mergeable, clean. Locally the dev reports 54 of 55 derived gate families run and exit 0, with check:dual-build-cjs-loads left to CI, which is green; every card-named gate reads the same at base and head.
  4. Accepted deviations: none beyond the series shape. The proposed next cut, the CRUD sequence area (about −200, after screening SEQUENCES_TABLE and TursoDriver's sequence overrides), is noted for cut 11.
  5. Out of scope: none filed.
  6. 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 26, 2026 19:23
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 19:23
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 817cb89 Oct 26, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-5jymjn branch October 26, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants