Skip to content

refactor(driver-sql): move the deferred-DDL previews out of sql-driver.ts into sql-driver-schema-sync.ts (#25521 cut 18) - #26894

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

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

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 里「延迟建表 DDL」小节的三个预览方法体(os migrate plan 用来列出待执行工作的 previewDeferredSchemaWork,以及它调用的日期时间、时间两类存储收敛预估 previewDatetimeConvergence、previewTimeConvergence)原样搬进已有的 sql-driver-schema-sync.ts;类上三个方法全部保留,只把方法体转交过去,SchemaSyncHost 按需增补 6 个成员。
  • 为什么改:[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 拆分超大文件的第 18 刀;第 17 刀合入后重新测量,这是仍成一个区域、收益最大且不碰任何系列限制的部分,sql-driver.ts 减少 74 行。
  • 风险与回滚:纯搬移,无行为变化;三个 SQL 驱动的测试两端逐条一致,公开类型声明哈希不变,41 条探针读数两端一致(仅 2 条栈顶接收者名不同)。回滚即还原本 PR。
  • 先看:sql-driver-schema-sync.ts 头注释新增的第 18 刀段落与 SchemaSyncHost 新增成员;sql-driver.ts 中三个方法的转交桩;sql-driver.ts 顶部 fieldHasColumn 改为类型导入的那一行。
  • 两分钟验证:pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-deferred-ddl.test.ts src/sql-driver-deferred-datetime-convergence.test.ts src/sql-driver-time-canonical-storage.test.ts src/sql-driver-6009-backfill-julian-day-guard.test.ts,预期全部通过。
  • 需要你做的:无,席位落地。

What changes

Cut 18 of the sql-driver.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6449288193. Cut 17 (#26808) merged first, as f1cd529dd2; this cut opens with a fresh seam table taken on that commit (below) and takes the area cut 17 priced next, which still pays best without breaking a series limit: the deferred-DDL previews (Schema Sync › Deferred schema DDL (#3917)). Their bodies move unedited into the existing seam module packages/drivers/driver-sql/src/sql-driver-schema-sync.ts, through its host SchemaSyncHost, beside the canonical-encoding convergence (cut 12) whose predicates and information_schema reads they call:

No module-level declaration moves. fieldHasColumn (from ./schema-drift.js) loses its last value reader in sql-driver.ts; the staying docblock of previewDeferredSchemaWork still links it, so the import there becomes type fieldHasColumn in place (a link-only import is a type import, erased from the build, as cut 17 did for undeliveredStorageAttributes). The module imports fieldHasColumn and type PendingSchemaWork from ./schema-drift.js through its existing import line; SQLITE_NON_TEMPORAL_TEXT_REFS and SQLITE_TIME_EXPR_REFS it already imported from ./sql-driver.js.

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, no test edit and no new gate. Branch claude/project-thread-m210nw (the platform assigned it to this card thread, as the claim records). The move is one commit, 028cda6cf, on f1cd529dd2, followed by one merge of origin/main (5787df4d4b, 15 commits, none in packages/drivers/**); the head is 7065d8e1d0.

Net line count (the seat's rule). sql-driver.ts goes from 18,521 lines to 18,447: -74.

  • Out: the three bodies, 119 lines plus their closing braces (45 + 37 + 37).
  • In: the three stub bodies, 43 lines (13 + 15 + 15), and 2 comment lines above the existing sql-driver-schema-sync.js import, whose one line gains the three names. The fieldHasColumn import line changes in place.
  • sql-driver-schema-sync.ts goes from 1,914 to 2,066 lines (+152): the three functions, 6 new SchemaSyncHost members (74 to 80) and a header paragraph. No import line is added.

Path readers. No test, script, doc, ADR or anchor entry reads any of the three bodies by path, so none is migrated (screen below).

Serial with the file's in-flight PRs. I read the file list of every open PR on objectstack-ai/objectstack at the start (38) and again before opening this one (40). None touches sql-driver.ts, sql-driver-schema-sync.ts, turso-driver.ts, sqlite-wasm-driver.ts, check-tenant-chokepoint.mjs or a driver-sql anchor entry; #26808 (cut 17) was the one that did, and it merged before this cut was measured. #26556 (dependabot) touches only driver-sql/package.json.

Seam table, fresh, on f1cd529dd2

  • Size and churn. sql-driver.ts is 18,521 lines. The 30-day column counts git blame lines from commits on or after 2026-09-27T17:26:58Z, 30 days before the base commit. scripts/pm/git-history.mjs count proves 77 commits on the file in 30 days and 31 in the last 7. In all, 3,388 lines (18.3%) are from the last 30 days.
  • Class shape. SqlDriver spans lines 4243-18446 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-17 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-4242 Module preamble - 4,242 692 - - - -
4243-5834 Class head: fields, statics, constructor, capability getters name … assertDialectEmits 1,592 290 8 (1) 1 0 18 (18)
5835-6266 Lifecycle connect … disconnect 432 155 8 (3) 0 0 6 (6)
6267-8673 CRUD — IDataDriver core find … delete 2,407 745 43 (7) 0 12 0 (0)
8674-9203 Bulk & Batch Operations bulkCreate … count 530 70 9 (6) 0 7 0 (0)
9204-9300 Raw Execution rawStatementFault … noteRuntimeIndexDdl 97 28 3 (1) 0 0 0 (0)
9301-9393 Transactions beginTransaction … rollbackTransaction 93 46 7 (5) 0 0 0 (0)
9394-9890 Aggregation aggregate … unresolvableAggregateColumnRefusal 497 70 5 (1) 0 1 0 (0)
9891-10152 Distinct distinct … unresolvableDistinctColumnRefusal 262 26 3 (1) 0 1 0 (0)
10153-10268 Window Functions findWithWindowFunctions … findWithWindowFunctions 116 4 1 (1) 0 1 0 (0)
10269-10367 Query Plan Analysis explain … analyzeQuery 99 1 2 (2) 0 1 0 (0)
10368-10426 Schema Sync (syncSchema / init) syncSchema … reclaimSpace 59 34 3 (3) 0 0 0 (0)
10427-11851 Schema Sync (syncSchema / init) › Data-lifecycle rotation (ADR-0057 P2) rotationStateByTable … migrateMysqlTimeColumns 1,425 219 26 (4) 0 0 0 (0)
11852-12108 Schema Sync (syncSchema / init) › Deferred schema DDL (#3917) setDeferredDdl … flushDeferredSchemaDdl 257 25 6 (4) 0 0 0 (0)
12109-12923 Schema Sync (syncSchema / init) › Managed-schema drift & reconcile (#2186) dialectName … createNullSafeUniqueIndex 815 237 23 (3) 0 0 0 (0)
12924-12975 Schema Introspection listPhysicalColumns … introspectSchema 52 46 2 (2) 0 0 0 (0)
12976-13246 Internal helpers getKnex … auditMissingTenant 271 73 8 (1) 0 1 0 (0)
13247-15296 Internal helpers › Filter helpers tableNameForBuilder … applyFalseConstant 2,050 331 44 (2) 0 0 0 (0)
15297-15472 Internal helpers › Field mapping mapSortField … mapAggregateFunc 176 2 6 (0) 0 0 0 (0)
15473-15520 Internal helpers › Window function builder buildWindowFunction … buildWindowFunction 48 26 1 (0) 0 0 0 (0)
15521-17467 Internal helpers › Column creation helper nowColumnDefault … applyDeclaredColumnDefault 1,947 92 19 (0) 0 0 8 (8)
17468-17765 Internal helpers › Database helpers sqliteFilename … isJsonField 298 76 9 (2) 0 0 0 (0)
17766-18242 Internal helpers › SQLite serialisation formatInput … formatOutput 477 27 2 (0) 0 0 0 (0)
18243-18446 Internal helpers › Introspection internals introspectColumnOrder … introspectUniqueConstraints 204 73 5 (0) 0 0 0 (0)
18447-18521 After the class - 75 0 - - - -

Pricing the areas

Eligibility is as cuts 11-17 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; not a member pinned in place (createColumn, applyFilterCondition, formatInput/formatOutput, initObjects, ensureShardTable, computeTenantField, migrateMysqlDatetimeColumns/migrateMysqlTimeColumns, compileFilters). Each area is priced as its stubs minus its bodies (a stub costs 4 lines, +2 per field read, +1 per method read; one reading no member costs 2), then net of the module-level declarations only it reads and of its import edits.

Area Bodies Bodies net Declarations and imports Disposition
Deferred-DDL previews (previewDeferredSchemaWork -32, previewDatetimeConvergence -22, previewTimeConvergence -22) 3 -76 0 declarations move (fieldHasColumn, PendingSchemaWork and the two SQLITE_* counts are shared); +2 for the import comment Moves (this cut). -74 measured.
Filter helpers remainder (14 small members, largest presentReadValue -29) 14 -80 0; +2 or more for the import comment Not taken: fourteen members of four mechanisms (presentation, withheld-filter log, canonical SQL, column expression), not one area; its largest coherent mechanism, the withheld-filter log (logWithheldFilterDiagnostic -10, resolveWithheldFilterRefusal -7, withWithheldFilterLog -6), pays -23. compileFilters (-75) is refused (inferred return type).
Class head (buildDateBucketExpr -42, installQueryTiming -20) 2 -62 0 Not taken: two unrelated mechanisms, and smaller.
Column creation helper remainder (nowColumnDefault -39 and four small members) 5 -62 0 Not taken: smaller; the rest of that sub-banner reads private statics.
Internal helpers tenant members (auditMissingTenant -26, applyTenantScope -25, getBuilder -5, injectTenantOnInsert -2) 4 -58 0 Not taken: smaller, and applyTenantScope holds 2 of the file's 4 D8 predicates and getBuilder is the builder itself, which check:tenant-chokepoint reads only in sql-driver.ts (series limit).
CRUD remainder (updateWithPresumedTimestamp -31 and seven small members) 8 -53 0 Not taken: smaller, and two mechanisms (autonumber probes, the presumed-timestamp update).

Why

Why this area, as one area

  • One banner, one job. The three are the members of the class's own Deferred schema DDL (#3917) sub-banner that compute the plan; previewDeferredSchemaWork is the only caller of the other two in the class. They go beside the convergence code (cut 12) whose predicates (sqliteNonTemporalTextSql, sqliteCanonicalDatetimeSql, sqliteCanonicalTimeSql) and information_schema reads (legacyMysqlTimestampColumns, legacyMysqlTimeColumns) they reuse, so the plan and the work it predicts now live in one module, behind the host that already exists.
  • It pays the most of any area that moves cleanly. The filter-helper remainder is larger on paper (-80) but is four mechanisms; no single one of them pays more than -29.
  • What stays, and why. setDeferredDdl and flushDeferredSchemaDdl assign this.deferredDdl / this.flushingDeferredDdl (this-write); getSchemaSyncStats reads two fields in one line (its stub would cost more than its body); deferredSchemaObjectCount is an accessor.
  • No series limit is touched. None of the three builds through getBuilder, reads a static, has a this.FIELD.delete/insert/update( site, uses super or a bare this, or assigns to this. previewDeferredSchemaWork is public with an annotated return (a promise of PendingSchemaWork[]); the other two are protected with the same annotated return. 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.
  • Durability gate. The two convergence previews swallow a failed probe to [] (by contract: under-reporting costs an unlisted step, never a wrong one). Their try blocks call no member of the gate's durability vocabulary, and check:durability-log-level --list gives the same seams at both ends, line numbers aside (164 lines of output at each end). I screened this before choosing the area, by moving the three bodies on cut 17's head and diffing the census.
  • Subclasses. TursoDriver and SqliteWasmDriver extend SqlDriver and override none of the three. TursoDriver's remote-face ledger lists previewDeferredSchemaWork as inherited, and turso-remote-inherited-members.test.ts checks that SqlDriver declares it and TursoDriver does not redeclare it; both still hold (the method keeps its name and signature on SqlDriver). 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 three by a sql-driver.ts#… pointer or a sql-driver.ts::… key, and no scripts/adr-anchors/** record names them. Tests and the CLI (packages/cli/src/utils/schema-migrate.ts) call them as driver methods, which they still are; turso-driver.ts and remote-canonical-backfill.ts name them in prose, which is still true.

The shape (#23050 shape B, as cuts 1-17 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 18]. Each method returns the moved async function's own promise, so the declared return type is unchanged and a failure is still a rejection, never a synchronous throw (probe reading 7).
  • The fields the bodies read (deferredSchemaObjects, knex, datetimeFields, timeFields, and the getters supportsRotation, isSqlite, isMysql) go as GETTERS that read the driver at call time; the methods (previewDatetimeConvergence, previewTimeConvergence, rotationShardKey, legacyMysqlTimestampColumns, legacyMysqlTimeColumns, sqliteCanonicalDatetimeSql, sqliteCanonicalTimeSql, sqliteNonTemporalTextSql) 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; in particular previewDeferredSchemaWork still reaches the two convergence previews through the driver, not the module functions directly.
  • SchemaSyncHost grows by exactly the 6 members these bodies read that it did not list yet (deferredSchemaObjects, legacyMysqlTimeColumns, legacyMysqlTimestampColumns, previewDatetimeConvergence, previewTimeConvergence, supportsRotation), each typed as the class member's indexed access, in the interface's sorted order (74 to 80 members). There is no cast, and no member becomes public.
  • Comments. The module header gains one [#25521 cut 18] paragraph saying what moved, what stayed and where each read declaration comes from. The sql-driver.ts import comment names cut 18 in 2 lines. Neither cites an ADR-NNNN letter. The moved previewDeferredSchemaWork body keeps its [#22506] comment, which mentions ADR-0057 P2 (not a decision letter; the count below is unchanged).
  • 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 1898 decision-letter citations at base f1cd529dd2 (a clean detached worktree) and 1898 at the move commit (a clean detached worktree), over 91 anchored files at both ends; the two outputs are byte-identical. Both runs exit 0.
  • Why equal. No comment inside the moved bodies or the new header paragraph 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. No anchor entry names a moved member. check:adr-symbol-anchors: 2687 resolve at both ends (output byte-identical).
  • After the merge. main moved under the cut, so I measured its tip 5787df4d4b against the merge head 7065d8e1d0: 1898 letters at both (91 anchored files, exit 0), and 2687 symbol anchors resolve at both.

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 base sql-driver-schema-sync.ts into this worktree under an EXIT/INT/TERM trap that restores HEAD. The restore is proved by the HEAD blob hashes, an empty git diff HEAD and 0 status entries (RESTORE-OK d2ff2d1b98… d29e275e18…).
  • 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), on the move commit and again on the merge head. These two are the dist's only declaration files (no chunk), so nothing was re-ordered and the declaration-reorder bar of ruling 6441032442 does not come into play.
  • Positive control. I dropped protected from previewDatetimeConvergence in the head index.d.ts (1 hit, then 0). The result hashes to fd2f04595d02e10a86dd26debff0b40b4f8e3428699c28383e74d5a64193c0ad, with cmp exit 1.
  • Export lists. CJS require of each dist gives 59 names (sha1 05757ce5ae at both ends); the ESM namespace of the base and head index.mjs gives the same 59 names (sha1 05757ce5ae both). None of the three moved functions or SchemaSyncHost is among them (fieldHasColumn is, at both ends: index.ts re-exports it from schema-drift.ts, which this cut does not touch).
  • The head dist carries the move. .call(host sites in index.mjs go from 64 at base to 67 at head (the three stubs).

Move-only proof

The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base f1cd529dd2 against the move commit 028cda6cf. It is a scratch script and is not committed.

Moved body Body tokens Comments Parameter tokens less this Return type tokens Body lines
previewDeferredSchemaWork 469 13 0 6 45
previewDatetimeConvergence 374 0 17 6 37
previewTimeConvergence 377 0 17 6 37
  • 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. All three are async at both ends.
  • The three stubs. Leading trivia (the docblock), modifiers and the signature are identical to base, less async.
  • The class. It has 350 members at both ends, in the same order, and the 347 unmoved members are byte-identical, trivia included.
  • Top level of sql-driver.ts. 177 of 180 base statements are byte-identical; the other three are the class and the two edited imports (fieldHasColumn gains type; the schema-sync import gains three names and its 2-line comment).
  • The module. Its 42 base statements are all in the head module byte-identical except three: its first import (whose leading trivia is the header, which gained the cut 18 paragraph), the ./schema-drift.js import (fieldHasColumn and type PendingSchemaWork added) and SchemaSyncHost (6 members added). The head adds the three functions.
  • Result. PROOF OK, exit 0; on the merge head its output is byte-identical.
  • Negative control. Through scripts/ablation-replace.mjs, I changed kind: 'widen_time_columns' to kind: 'widen_time_columnz' in the module's previewTimeConvergence (anchor 1 hit, then 0, checked on disk). The proof then reports previewTimeConvergence as differing, prints PROOF FAILED, and exits 1. The restore was proved (blob d29e275e18 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 f1cd529dd2 against the move commit 028cda6cf, and then again on the merge head 7065d8e1d0. Every part's exit code was captured on its own (build, probe, three suites: all 0 at every leg).

Suite Base Head Lost / changed / added
@objectstack/driver-sql 4061 (3847 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

On the merge head (it brings in main up to 5787df4d4b: 15 commits, none in packages/drivers/**), the three suites give the same totals and the same per-test statuses as base, 0 / 0 / 0 in each.

  • Type check and lint. tsc --noEmit -p . in driver-sql exits 0 on the move commit and on the merge head. 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 and prints 41 readings, each with the result or the error's message, code, status, own keys and top stack frame, plus the tables sqlite_master holds, the stored cells and the log lines:
    1. previewDeferredSchemaWork on SQLite: a table not created yet; legacy datetime and time rows (epoch numbers, space-separated and offset strings, a julian-day real, junk text, null) beside an added column and two formula fields; a second preview (idempotent); the flush (the plan it returns, the rewritten cells) and a re-plan after it; a canonical table with two new ones (the sort);
    2. the os migrate apply --allow-destructive cannot drop sys_account.issuer on a 17.4.0-created SQLite database, while os migrate account-issuer and the boot's schema-drift line keep prescribing it (17.7.0) #22506 rotation answer: no shard yet, the flush, current shard plus read view present, a view without a shard, and a rotation policy without a unit;
    3. previewDatetimeConvergence and previewTimeConvergence called directly with every column subset, an unknown table, a declared table that does not exist, and bad arguments;
    4. both on unreachable Postgres and MySQL drivers (probe failures swallowed to []);
    5. the MySQL branch with legacyMysqlTimestampColumns / legacyMysqlTimeColumns stubbed on the instance and a fake knex answering the count in both result shapes, with none, and throwing;
    6. a SUBCLASS overriding the two convergence previews, rotationShardKey, the three SQLite predicates and the supportsRotation getter; then instance spies on two of them; then datetimeFields and deferredSchemaObjects replaced after construction, knex behind a getter, isSqlite forced false, and a null deferral map (the new ones are the ones reached, in the same order);
    7. the async shape: a failure is a rejected promise, not a synchronous throw, and each method returns a promise.
  • Probe result. 39 of the 41 readings are identical, results, call orders, cells and tables included. The other 2 differ only in the top stack frame's receiver name, where a null deferral map throws inside the moved body: Sub.previewDeferredSchemaWork / _SqlDriver.previewDeferredSchemaWork at base and Object.previewDeferredSchemaWork at head. That is shape B's receiver, the same at every earlier cut, and no test or consumer reads it. One reading depends on the calendar day (the shard name of the flushed rotation object); every leg ran on 2026-10-27. On the merge head the probe output is byte-identical to the move commit's.
  • Not run. No ablation, because no tested behaviour changed.
  • test:repo. Not run locally; it is CI's run on this PR.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 48 families for this diff, the same list on the move commit and on the merge head (and the same 48 cut 17 derived). I ran each one on its own and captured its exit code before any pipe, after a full pnpm build of that tree (116 of 116 tasks).

  • Move commit 028cda6cf: 48 run, all 48 exit 0. --ran reconciles: 48 derived famil(ies) accounted for — 48 run, 0 NOT-MEASURED, exit 0. (I fetched the pinned fixture commit of check-plugin-teardown-shape.mjs --self-test into this shallow clone first, as cut 17 learned to.)
  • Merge head 7065d8e1d0: 48 run, all 48 exit 0. --ran reconciles the same way, exit 0.
  • Hand gates on both heads, all exit 0: check-adr-anchors, check-adr-symbol-anchors, check-tenant-audit-census (244 write call sites), check-platform-checklist --changed-from origin/main, check-commit-card-trailers --range origin/main..HEAD, eslint --no-inline-config packages/drivers/driver-sql, check-durability-degradation-log-level --list. In clean detached worktrees at base f1cd529dd2 and at the move commit, check-adr-anchors, check-adr-symbol-anchors, check-tenant-audit-census and check-object-def-param-keys print byte-identical output, and check-durability-degradation-log-level --list prints the same seams, line numbers aside.
  • No gate silently empty. Each derived gate's log prints a non-zero population (for example check:object-def-param-keys compares 7 override parameter positions over 3370 files with every control firing; check:tenant-chokepoint reads 24 getBuilder() bindings across 3 files; check:durability-log-level reads 88 read seams; check:dual-build-cjs-loads loads 112 entry points across 69 packages).

The 48 derived commands: node scripts/check-closing-keyword-parity.mjs, node scripts/check-closing-keyword-parity.mjs --self-test, node scripts/check-comment-mask-adoption.mjs, node scripts/check-comment-mask-adoption.mjs --self-test, node scripts/check-comment-mask-corpus.mjs, node scripts/check-dts-emitted.mjs --self-test, node scripts/check-issue-citations.mjs, node scripts/check-plugin-teardown-shape.mjs, node scripts/check-plugin-teardown-shape.mjs --self-test, node scripts/ci/assert-turbo-remote-cache.mjs --self-test, node scripts/ci/plan-test-core-shards.mjs --self-test, node scripts/ci/prune-turbo-cache.mjs --self-test, node scripts/docs-audit/check-affected-docs.mjs, node scripts/docs-audit/check-drift-comment.mjs, pnpm --filter @objectstack/spec run check:duration-unit-keys, pnpm check:ci-filter-parity, pnpm check:closed-ceremony-text, pnpm check:cross-package-test-inputs, pnpm check:dispatcher-error-vocabulary, pnpm check:doc-authoring, pnpm check:driver-conformance, pnpm check:driver-memory-census, pnpm check:dts-closure, pnpm check:dual-build-cjs-loads, pnpm check:durability-log-level, pnpm check:error-status-conformance, pnpm check:gitlink-declared, pnpm check:issue-citations, pnpm check:keyed-text-bounds, pnpm check:lean-entry-closure, pnpm check:logger-receiver-detach, pnpm check:nul-bytes, pnpm check:object-def-param-keys, pnpm check:org-identifier, pnpm check:page-declaration-shape, pnpm check:platform-object-tenancy-census, pnpm check:published-files, pnpm check:refd-timer-probe, pnpm check:registry-log-declared, pnpm check:rest-log-spy-declared, pnpm check:slot-lookup, pnpm check:sourcemap-no-sources-content, pnpm check:system-context-census, pnpm check:tenant-chokepoint, pnpm check:test-source-alias, pnpm check:tier-file-adoption, pnpm check:undeclared-dep-imports, pnpm check:watch-hint-literal.

Acceptance notes

  • check:tenant-chokepoint's SCAN_FILES does not list sql-driver-schema-sync.ts. The moved bodies hold 0 of the file's 4 tenant predicates and build no query through getBuilder (they read sqlite_master, columnInfo and row counts through knex). Noted, not filed: widening SCAN_FILES would be a gate change.
  • Stale links from cut 15 are unchanged. sql-driver.ts has the same 30 {@link X} sites (21 names) that are neither a top-level binding nor a SqlDriver member at base and at head (the set is identical); this cut adds none, because fieldHasColumn stays linkable through its type import.
  • The series' remaining yield, priced on this base. After this cut, the largest single area that moves cleanly is about -62 (the class head's date-bucket and query-timing pair, or the column-creation remainder); the filter-helper remainder (-80) splits into four mechanisms of -29 or less each. Every further area pays between about -30 and -62.

Human Minimum

  • 1. No packages/spec change.
  • 2. No new name; SchemaSyncHost gains members only.
  • 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_01FEkQ5kNG4UuwLvtVwPNQqK

…r.ts into sql-driver-schema-sync.ts

previewDeferredSchemaWork, previewDatetimeConvergence and previewTimeConvergence
move unedited into the existing schema-sync module behind SchemaSyncHost; the
class keeps all three methods as delegating stubs (shape B). fieldHasColumn's
last value reader moved, so sql-driver.ts imports it as a type for the
staying docblock link. sql-driver.ts goes from 18,521 to 18,447 lines.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/deployment/cli.mdx (via add_columns (literal, a string literal in previewDeferredSchemaWork), create_table (literal, a string literal in previewDeferredSchemaWork), normalize_datetime_storage (literal, a string literal in previewDatetimeConvergence), widen_datetime_columns (literal, a string literal in previewDatetimeConvergence))
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 a51ba639c165861a8c4ac51015e554911960bf24 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 a51ba639c165861a8c4ac51015e554911960bf24 → 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

Seat CI note

2026-10-27T20:20Z · domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · head 7065d8e1d0

Test Core (6/8) (job 122437115374) failed: @objectstack/plugin-security pnpm run test exited 1. Every other check-run on this head is green or still running.

The seat judges this a flake, and will re-run the failed jobs ONCE. A second failure is real.

  • The diff: it moves three read-only preview bodies (previewDeferredSchemaWork, previewDatetimeConvergence, previewTimeConvergence) out of sql-driver.ts. Their only callers are the os migrate plan (packages/cli/src/utils/schema-migrate.ts) and driver-turso. No plugin-security source or test calls them; the package uses SqlDriver only through the boot and CRUD paths, which this diff does not touch.
  • A known flake: plugin-security carries a named queue-flake anchor, Queue-flake anchor: src/delegated-organization-membership.test.ts #26828 (src/delegated-organization-membership.test.ts, two independent queue ejections in 24 hours).
  • What could not be read: this session cannot read the failing file name, because the job-log blob answers 403 and the tail it can read stops after the package's output. If the re-run fails again, the seat reads it as real and sends it back to the card thread to reproduce locally.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-27T20:39Z · head 7065d8e1d0 · #25521 cut 18

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

  1. Shape:

    • A draft against main, assignee os-project-manager, labelled skip-changeset because this is a move.
    • The body opens with Part of #25521 (not Fixes). Clause-②: no and Metadata-protocol: none are line-initial, with Ruling-ref: 6270097840 and 6348891160.
    • check-governed-merges --pr 26894 reads NOT governed, 328 changed lines.
    • The claim (6449288193) names this PR's branch.
  2. Premise: the cut takes the slice cut 17's acceptance named: the deferred-DDL previews previewDeferredSchemaWork, previewDatetimeConvergence and previewTimeConvergence. They were re-measured on f1cd529dd2 and go into the existing sql-driver-schema-sync.ts through SchemaSyncHost. sql-driver.ts goes from 18,521 to 18,447 lines (net −74).

  3. Move check (the seat's own): every non-blank line removed from sql-driver.ts against origin/main was compared with the lines added to sql-driver-schema-sync.ts, indentation stripped. Every one is present there except these:

    • the ./sql-driver-schema-sync.js import line, which gains the three names;
    • fieldHasColumn, which becomes a type-only import in sql-driver.ts because its last value reader moved, and is imported by the module from schema-drift.ts;
    • the three method heads, which drop async. Each returns the moved async function's promise, with the explicit Promise<PendingSchemaWork[]> return types unchanged.

    What sql-driver.ts gains is three delegating bodies with their hosts. Fields go as getters, and methods (previewDatetimeConvergence, previewTimeConvergence, rotationShardKey, the legacyMysql* and sqlite*Sql readers) as late-bound arrow delegates. So a driver-turso subclass override is still the one reached. SchemaSyncHost grew by exactly the six members the moved bodies read. No moved body assigns a field.

  4. Published .d.ts: the raw sha256 is 0f33bf3f… at base and at head, and the positive control changes it. check:adr-anchors counts 1,898 at both base and head.

  5. Overlap: git merge-tree exits 0 against main eab4bbaf. The dev found no open PR on driver-sql before pr_create.

  6. Gates:

    • Test Core (6/8) failed on attempt 1, in @objectstack/plugin-security, which this diff does not reach. The seat judged it a flake in 6452640167 and re-ran the failed jobs once; attempt 2 is green.
    • At this stamp every check-run on the head reads success or skipped: 34 success, 16 skipped, none failed or in progress.
    • Locally the dev reports:
      • 48 of 48 derived gates plus 7 hand gates exit 0, with 0 NOT-MEASURED;
      • the three driver suites match base;
      • the AST proof is OK, and its negative control fails as it should.
  7. Next: the dev measures the largest clean single area remaining at about −62. The −80 filter-helper remainder is four mechanisms of −29 or less. One more cut clears the −30 bar; after that the series is near its end.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m 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