Skip to content

refactor(driver-sql): move the data-lifecycle rotation sub-banner's movable members out of sql-driver.ts into sql-driver-schema-sync.ts (#25521 cut 17) - #26808

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

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

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 里「数据生命周期轮转」小节中能搬的五个方法体(轮转器 ensureRotation 及它调用的分片键 rotationShardKey、视图列清单 rotationColumnList,三条建表路径共用的内置列冲突告警 warnBuiltinColumnCollisions,联邦外部对象的免 DDL 注册 registerExternalObject)原样搬进已有的 sql-driver-schema-sync.ts;类上五个方法全部保留,只把方法体转交过去,SchemaSyncHost 按需增补成员。
  • 为什么改:[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 拆分超大文件的第 17 刀;重新测量后这是收益最大且不碰任何系列限制的区域,sql-driver.ts 减少 125 行。
  • 风险与回滚:纯搬移,无行为变化;三个 SQL 驱动的测试两端逐条一致,公开类型声明哈希不变,121 条探针读数两端一致(仅 8 条栈顶接收者名不同)。回滚即还原本 PR。
  • 先看:sql-driver-schema-sync.ts 头注释新增的第 17 刀段落与 SchemaSyncHost 新增成员;sql-driver.ts 中五个方法的转交桩;sql-driver.ts 顶部两处 import 的改动。
  • 两分钟验证:pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-rotation.test.ts src/sql-driver-ddl-gate.test.ts src/sql-driver-external-columnmap.test.ts src/sql-driver-shard-tenancy-opt-out.test.ts,预期全部通过。
  • 需要你做的:无,席位落地。

What changes

Cut 17 of the sql-driver.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6446054910. The cut opens with a fresh seam table (below), taken on origin/main 785a7c3b3e, and takes the area cut 16 priced next and that still pays best without breaking a series limit: the movable members of the data-lifecycle rotation sub-banner (Schema Sync › Data-lifecycle rotation). Their bodies move unedited into the existing seam module packages/drivers/driver-sql/src/sql-driver-schema-sync.ts, the module cut 12 already moved six members of this same sub-banner into, through its host SchemaSyncHost:

No module-level declaration moves: none is read by these bodies alone. declaredValueShapes is also read by registerManagedObjectMetadata, which stays, so it gains a leading export in place and the module imports it. The three names of ./builtin-column-collision.js (undeliveredStorageAttributes, formatAttribute, type UndeliveredAttribute) lose their last value reader in sql-driver.ts; the import there shrinks to type undeliveredStorageAttributes, which the staying docblock of warnBuiltinColumnCollisions links (a link-only import is a type import, erased from the build).

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-0gietk (the platform assigned it to this card thread, as the claim records). The move is one commit, 6b6ca4b01, on 785a7c3b3e, followed by one merge of origin/main (83c4bd29f7); the head is 3dcd74655b.

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

  • Out: the five bodies, 192 lines (87 + 67 + 28 + 7 + 3), and 4 import lines (the three-name builtin-column-collision.js import becomes one line).
  • In: the five stub bodies, 68 lines (44 + 14 + 6 + 2 + 2), and 3 comment lines above the existing sql-driver-schema-sync.js import, whose one line gains the five names.
  • sql-driver-schema-sync.ts goes from 1,653 to 1,914 lines (+261): the five functions, 20 new SchemaSyncHost members, three imports and a header paragraph.

Path readers. No test, script, doc, ADR or anchor entry reads any of the five 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 (27) and again before opening this one (26). None touches sql-driver.ts, sql-driver-schema-sync.ts, turso-driver.ts, sqlite-wasm-driver.ts, check-tenant-chokepoint.mjs, check-object-def-param-keys.mjs or a driver-sql anchor entry. #26556 (dependabot) touches only driver-sql/package.json.

Seam table, fresh, on 785a7c3b3e

  • Size and churn. sql-driver.ts is 18,646 lines. The 30-day column counts git blame lines from commits on or after 2026-09-27T15:00:18Z, 30 days before the base commit. scripts/pm/git-history.mjs count proves 76 commits on the file in 30 days and 31 in the last 7. In all, 3,323 lines (17.8%) are from the last 30 days.
  • Class shape. SqlDriver spans lines 4244-18571 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-16 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-4243 Module preamble - 4,243 688 - - - -
4244-5835 Class head: fields, statics, constructor, capability getters name … assertDialectEmits 1,592 290 8 (1) 1 0 18 (18)
5836-6267 Lifecycle connect … disconnect 432 155 8 (3) 0 0 6 (6)
6268-8674 CRUD — IDataDriver core find … delete 2,407 745 43 (7) 0 12 0 (0)
8675-9204 Bulk & Batch Operations bulkCreate … count 530 70 9 (6) 0 7 0 (0)
9205-9301 Raw Execution rawStatementFault … noteRuntimeIndexDdl 97 28 3 (1) 0 0 0 (0)
9302-9394 Transactions beginTransaction … rollbackTransaction 93 46 7 (5) 0 0 0 (0)
9395-9891 Aggregation aggregate … unresolvableAggregateColumnRefusal 497 70 5 (1) 0 1 0 (0)
9892-10153 Distinct distinct … unresolvableDistinctColumnRefusal 262 26 3 (1) 0 1 0 (0)
10154-10269 Window Functions findWithWindowFunctions … findWithWindowFunctions 116 4 1 (1) 0 1 0 (0)
10270-10368 Query Plan Analysis explain … analyzeQuery 99 1 2 (2) 0 1 0 (0)
10369-10427 Schema Sync (syncSchema / init) syncSchema … reclaimSpace 59 34 3 (3) 0 0 0 (0)
10428-11976 Schema Sync (syncSchema / init) › Data-lifecycle rotation (ADR-0057 P2) rotationStateByTable … migrateMysqlTimeColumns 1,549 158 26 (4) 0 0 0 (0)
11977-12233 Schema Sync (syncSchema / init) › Deferred schema DDL (#3917) setDeferredDdl … flushDeferredSchemaDdl 257 25 6 (4) 0 0 0 (0)
12234-13048 Schema Sync (syncSchema / init) › Managed-schema drift & reconcile (#2186) dialectName … createNullSafeUniqueIndex 815 237 23 (3) 0 0 0 (0)
13049-13100 Schema Introspection listPhysicalColumns … introspectSchema 52 46 2 (2) 0 0 0 (0)
13101-13371 Internal helpers getKnex … auditMissingTenant 271 73 8 (1) 0 1 0 (0)
13372-15421 Internal helpers › Filter helpers tableNameForBuilder … applyFalseConstant 2,050 331 44 (2) 0 0 0 (0)
15422-15597 Internal helpers › Field mapping mapSortField … mapAggregateFunc 176 2 6 (0) 0 0 0 (0)
15598-15645 Internal helpers › Window function builder buildWindowFunction … buildWindowFunction 48 26 1 (0) 0 0 0 (0)
15646-17592 Internal helpers › Column creation helper nowColumnDefault … applyDeclaredColumnDefault 1,947 92 19 (0) 0 0 8 (8)
17593-17890 Internal helpers › Database helpers sqliteFilename … isJsonField 298 76 9 (2) 0 0 0 (0)
17891-18367 Internal helpers › SQLite serialisation formatInput … formatOutput 477 27 2 (0) 0 0 0 (0)
18368-18571 Internal helpers › Introspection internals introspectColumnOrder … introspectUniqueConstraints 204 73 5 (0) 0 0 0 (0)
18572-18646 After the class - 75 0 - - - -

Pricing the areas

Eligibility is as cuts 11-16 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, then net of the module-level declarations only it reads (they would move along) and of its import edits.

Area Bodies Bodies net Declarations and imports Disposition
Rotation sub-banner (ensureRotation -53, registerExternalObject -43, warnBuiltinColumnCollisions -22, rotationShardKey -5, rotationColumnList -1) 5 -124 0 declarations move; -4 for the collision import, +3 for the import comment Moves (this cut). -125 measured.
Deferred-DDL previews (previewDeferredSchemaWork -32, previewDatetimeConvergence -22, previewTimeConvergence -22) 3 -76 0 (PendingSchemaWork, fieldHasColumn and the two SQLITE_* constants are shared) Not taken: smaller. Priced as the next slice.
Filter helpers remainder (14 small members, largest presentReadValue -29) 14 -80 0 Not taken: fourteen members of four mechanisms (presentation, withheld-filter log, canonical SQL, column expression), not one area; 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.

Why

Why this area, as one area

  • One banner. The five are the members of the class's own Data-lifecycle rotation sub-banner that can move. Cut 12 moved six other members of the same sub-banner (the SQLite backfills and the MySQL widening's reads and runner) into sql-driver-schema-sync.ts, so this cut puts them beside those, behind the host that already exists, instead of opening a new module.
  • It pays the most of any area that moves cleanly, about 1.6 times the next one.
  • What stays, and why.
    • rotateShards (its stub would cost as many lines as its body), aliasShardBookkeeping (its stub would cost more than its body), ensureShardTable (named by check:object-def-param-keys), computeTenantField (named by packages/spec/liveness/object.json), computeAndRecordTenantField and registerManagedObjectMetadata (each has a this.FIELD.delete( site, the tenant-audit series limit), initObjects (pinned), and migrateMysqlDatetimeColumns/migrateMysqlTimeColumns (each catch around a vocabulary callee reports through logDurabilityFailure, which check:durability-log-level resolves only within one file).
    • The accessors and the three one-line readers of rotationStateByTable stay; each stub would cost more than its body.
  • No series limit is touched. None of the five builds through getBuilder, reads a static, has a this.FIELD.delete/insert/update( site, uses super or a bare this, or assigns to this. registerExternalObject is public with an annotated void return; the other 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 moved bodies (registerExternalObject names tenantField four times, as a value it stores, never as a predicate).
  • Durability gate. The moved bodies have no catch clause. check:durability-log-level --list gives the same seams at both ends, line numbers aside (164 lines of output at each end).
  • Logger. ensureRotation and warnBuiltinColumnCollisions and registerExternalObject write to the logger through this.logger, which the host passes as a getter, so each call keeps its receiver. check:logger-receiver-detach is green (below).
  • Subclasses. TursoDriver and SqliteWasmDriver extend SqlDriver and override none of the five (TursoDriver calls the inherited registerExternalObject). Every stub keeps its signature and modifier, and every member a body calls is a late-bound closure on the instance (probe below).
  • Path readers, screened. No script, test, doc or ADR names any of the five by a sql-driver.ts#… pointer or a sql-driver.ts::… key, and no scripts/adr-anchors/** record names them. check-object-def-param-keys.mjs cites #ensureRotation as a symbol anchor in prose; the method keeps its name and signature in sql-driver.ts, and the gate's output is byte-identical at both ends. The tests that read sql-driver.ts as text look for createColumn, the width constants, the INVALID_FILTER builders and log-call shapes, none of which moved. Prose mentions elsewhere (turso-driver.ts, remote-transport.ts, ADR-0015 and ADR-0062, the objectql federation tests) name SqlDriver.registerExternalObject, which is still true.

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

  • Each class method keeps its docblock and signature, byte-identical. Its body is now one host literal plus NAME.call(host, ...), marked [#25521 cut 17]. rotationShardKey and rotationColumnList read no member of the driver, so their methods call the function directly. ensureRotation loses async and returns the moved function's own promise, as cut 12's methods do.
  • The fields the bodies read (knex, logger, rotationStateByTable, isSqlite and the seventeen per-object bookkeeping maps) go as GETTERS that read the driver at call time; the methods (ensureShardTable, aliasShardBookkeeping, rotationShardKey, rotationColumnList, computeAndRecordTenantField, isJsonField) 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.
  • SchemaSyncHost grows by exactly the 20 members these bodies read that it did not list yet, each typed as the class member's indexed access, in the interface's sorted order (54 to 74 members). There is no cast, and no member becomes public.
  • Module imports added. aggregandColumnClass (@objectstack/core); FILE_REFERENCE_TYPES, NUMERIC_VALUE_TYPES, parseAutonumberFormat, resolveAutonumberFormat, type AutonumberToken (@objectstack/spec/data); formatAttribute, undeliveredStorageAttributes, type UndeliveredAttribute (./builtin-column-collision.js); and declaredValueShapes, NUMERIC_SCALAR_TYPES added to the existing ./sql-driver.js import. The module reads them only inside function bodies, so the import cycle stays evaluation-safe. index.ts names its exports one by one; the export lists below prove none of them leaks.
  • Comments. The module header gains one [#25521 cut 17] paragraph saying what moved, what stayed and where each read declaration comes from. The sql-driver.ts import comment names cut 17 in 3 lines. Neither cites an ADR-NNNN letter. The line comments inside registerExternalObject's body that link {@link mediaFields} and its siblings now name SchemaSyncHost members of the same names.
  • 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. registerExternalObject's one multi-line parameter type is re-indented the same way.

Verification

ADR anchors

  • The count. check:adr-anchors counts 1898 decision-letter citations at base 785a7c3b3e (a clean detached worktree) and 1898 at head, over 91 anchored files at both ends. 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.
  • After the merge. main moved under the cut, so I measured its tip 83c4bd29f7 against the merge head 3dcd74655b: 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.
  • 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 ensureRotation in the head index.d.ts. The result hashes to 7d72d5f631e8e134fe0cfc67e9fcaa78bc3cddd2b5702c2754502ec0bcff8987, with cmp exit 1.
  • Export lists. CJS require of each dist gives 59 names (sha1 05757ce5ae at both ends); the ESM export statement of the base and head index.mjs lists the same 59 names (sha1 05757ce5ae both). None of the five moved functions, declaredValueShapes or SchemaSyncHost is among them.
  • The head dist carries the move. .call(host sites in index.mjs go from 61 at base to 64 at head (three host stubs; the two direct-call stubs have none).

Move-only proof

The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base 785a7c3b3e against head 6b6ca4b01. It is a scratch script and is not committed.

Moved body Body tokens Comments Parameter tokens less this Return type tokens Body lines
registerExternalObject 759 16 48 1 87
ensureRotation 557 14 57 24 67
warnBuiltinColumnCollisions 179 1 26 3 28
rotationShardKey 99 1 10 1 7
rotationColumnList 69 0 13 1 3
  • 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. ensureRotation is async at both ends; the other four are not.
  • The five stubs. Leading trivia (the docblock), modifiers and the signature are identical to base (ensureRotation less async).
  • The class. It has 350 members at both ends, in the same order, and the 345 unmoved members are byte-identical, trivia included.
  • Top level of sql-driver.ts. 176 of 180 base statements are byte-identical; the other four are the class, declaredValueShapes (it differs from base by a leading export and nothing else, checked), and the two edited imports.
  • The module. Its 34 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 17 paragraph), the ./sql-driver.js import (two names added) and SchemaSyncHost (20 members added). The head adds three imports and the five functions.
  • Result. PROOF OK, exit 0.
  • Negative control. Through scripts/ablation-replace.mjs, I changed week: 7 * 86_400_000 to week: 8 * 86_400_000 in the module's ensureRotation (1 hit, checked on disk). The proof then reports ensureRotation as differing, prints PROOF FAILED, and exits 1. The restore was proved (blob equals HEAD, empty git diff HEAD).

Tests

Each end ran under one os-verify-lock hold, after building that end's driver-sql dist. I compared the vitest JSON per test name and status, base 785a7c3b3e against head 6b6ca4b01, and then again on the merge head 3dcd74655b. 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 83c4bd29f7: 14 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 121 readings, each with the result or the error's message, code, status, own keys and top stack frame, the tables sqlite_master holds, the bookkeeping maps written, and the log lines:
    1. rotationShardKey over day, week, month and an unknown unit, across Sunday/Monday, month, year and leap-day edges, and a NaN instant;
    2. rotationColumnList with no fields, builtin names, an odd column name, and null;
    3. warnBuiltinColumnCollisions over the three phases and an unknown one, with no fields, a presentation-only collision, undelivered storage attributes and a null declaration;
    4. ensureRotation through rotateShards on SQLite across seven sweeps (new shards, the window, drops, rows written and counted through the view), weekly and monthly policies, the legacy base-table adoption, the partial-failure recovery (base table and current shard both present), a direct call, and a builtin-collision field on the shard path;
    5. registerExternalObject on SQLite, Postgres and MySQL drivers: every field class, remoteName/remoteSchema (warned and ignored on SQLite, stored elsewhere), a columnMap with a non-string entry, autonumber formats, a tenancy opt-out and its sticky re-registration, a declared tenant field, sys_organization, and bad input;
    6. a SUBCLASS overriding rotationShardKey, rotationColumnList, ensureShardTable, aliasShardBookkeeping, computeAndRecordTenantField, isJsonField and warnBuiltinColumnCollisions; instance spies on six of them; the logger, four bookkeeping maps and rotationStateByTable replaced after construction, and knex behind a getter (the new ones are the ones reached);
    7. bad arguments.
  • Probe result. 113 of the 121 readings are identical, results, tables, maps and log lines included. The other 8 differ only in the top stack frame's receiver name, where bad input throws inside a moved body: _SqlDriver.X at base and Object.X (host stubs) or X (direct-call stubs) 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 boot shard initObjects creates on the real clock is dropped by the first pinned sweep); 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. I ran each one on its own and captured its exit code before any pipe.

  • Merge head 3dcd74655b: 48 run, all 48 exit 0. --ran reconciles: 48 derived famil(ies) accounted for — 48 run, 0 NOT-MEASURED, exit 0.

  • Move commit 6b6ca4b01: 46 exited 0 on the first pass. Two exited 3, a missing prerequisite and not a verdict: node scripts/check-plugin-teardown-shape.mjs --self-test could not read its pinned fixture commit in this shallow clone, and passed (48 cases) once that commit was fetched; check:dual-build-cjs-loads needed a full workspace build, and passed (112 require entry points across 69 packages load) after pnpm build.

  • Hand gates on both heads, all exit 0: check-adr-anchors, check-adr-symbol-anchors, check-tenant-audit-census (244 write call sites at both ends), 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 (same seams both ends). check-object-def-param-keys and check-tenant-audit-census print byte-identical output at base and head.

  • 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 3362 files with every control firing; check:tenant-chokepoint reads the getBuilder() bindings of its scan files; check:durability-log-level reads 88 read seams).

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

  • logger-receiver-detach.test.ts's structural pin reads sql-driver.ts only. Three log calls moved to sql-driver-schema-sync.ts (as cut 12's did); the repo-wide check:logger-receiver-detach gate still reads them there and is green. Noted, not filed.
  • 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. Noted, not filed: widening SCAN_FILES would be a gate change.
  • Stale links from cut 15 are unchanged. sql-driver.ts still has the same 30 {@link X} 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 undeliveredStorageAttributes stays linkable through its type import.
  • Next slice, priced on this base. The deferred-DDL previews (previewDeferredSchemaWork, previewDatetimeConvergence, previewTimeConvergence, about -76) are now the largest clean area. After them the series' remaining yield outside the pinned methods is small, scattered bodies of about -60 or less per area.

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_01FEkQ5o3WH4EyHws8N24jPx

…into sql-driver-schema-sync.ts

ensureRotation, rotationShardKey, rotationColumnList, warnBuiltinColumnCollisions
and registerExternalObject move unedited into sql-driver-schema-sync.ts; the
class keeps each method as a shape-B stub through SchemaSyncHost.
declaredValueShapes gains export in place. Move-only.

Claude-Session: https://claude.ai/code/session_01FEkQ5o3WH4EyHws8N24jPx
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 52 documentable anchor(s).

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

  • content/docs/api/wire-format.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/automation/webhooks.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/data-modeling/queries.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/data-modeling/seed-data.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/deployment/cli.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/deployment/seed-tenancy-repair.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/deployment/troubleshooting.mdx (via auto_number (literal, a string literal in registerExternalObject))
  • content/docs/permissions/system-context.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/protocol/kernel/http-protocol.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/protocol/kernel/realtime-protocol.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/protocol/objectql/schema.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/protocol/objectql/security.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/protocol/objectql/state-machine.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/ui/views.mdx (via updated_at (literal, a string literal in rotationColumnList))

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

  • content/docs/releases/v15.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/releases/v16.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/releases/v17/17-3.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/releases/v17/17-5.mdx (via updated_at (literal, a string literal in rotationColumnList))
  • content/docs/releases/v17/17-6.mdx (via registerExternalObject (symbol, a method of class SqlDriver; a top-level function), auto_number (literal, a string literal in registerExternalObject), updated_at (literal, a string literal in rotationColumnList))
  • content/docs/releases/v17/17-7.mdx (via updated_at (literal, a string literal in rotationColumnList))

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

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 39 pages)
  • 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 c8583f80ac47177421c9c1fb71e5e33e431ad9cf → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 c8583f80ac47177421c9c1fb71e5e33e431ad9cf → 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-27T17:23Z · head 3dcd74655b · #25521 cut 17

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.
    • check-governed-merges --pr 26808 reads NOT governed, 538 changed lines. None of the four contract faces is touched, so no at-tier review is owed.
    • The claim (6446054910) names this PR's branch.
  2. Premise:

    • The seam table is re-measured fresh on 785a7c3b3e and is in the body.
    • The cut takes the rotation sub-banner's five movable members: ensureRotation, rotationShardKey, rotationColumnList, warnBuiltinColumnCollisions and registerExternalObject. They go into the existing sql-driver-schema-sync.ts through SchemaSyncHost.
    • sql-driver.ts goes from 18,646 to 18,521 lines.
  3. Move check (the seat's own): every line removed from sql-driver.ts on the move commit 6b6ca4b01 was compared with the lines added to sql-driver-schema-sync.ts, indentation stripped. All 188 non-blank removed lines are present there except these:

    • Two import blocks lose the names only the moved bodies read (formatAttribute, UndeliveredAttribute, undeliveredStorageAttributes), and the module imports those itself.
    • declaredValueShapes gains export in place. Staying code shares it, under cut 14's shared-helper ruling.
    • ensureRotation's delegating method drops async. It returns the moved async function's promise, so the declared return type is unchanged.

    Everything added to sql-driver.ts is the five delegating methods with their hosts. Fields go as read-only getters and methods as arrow delegates. rotationShardKey and rotationColumnList read no driver member, so they are called directly. SchemaSyncHost grew by exactly the members the moved bodies read. No moved body assigns a field.

  4. Path readers: none migrated. The body's screen finds no sql-driver.ts#… or ::… pointer to the five. The one symbol-anchor citation, check-object-def-param-keys.mjs → #ensureRotation, still resolves, because the method keeps its name in the class. That gate's output is byte-identical at both ends.

  5. Published .d.ts: the raw sha256 of dist/index.d.ts and dist/index.d.mts is 0f33bf3f… at base and at head, on both the move commit and the merge head. The positive control changes it. Nothing re-orders, so 6441032442's bar does not come into play. check:adr-anchors counts 1,898 at both base and head, and again at 83c4bd29f7 against the merge head.

  6. Overlap: none of the 60 most recently updated open PRs touches packages/drivers/driver-sql/src/sql-driver* (seat's read at this stamp).

  7. Gates:

    • At this stamp every check-run on the head reads success or skipped; none reads failed or in progress.
    • Locally the dev reports:
      • 48 of 48 derived gates exit 0 on the merge head, with 0 NOT-MEASURED;
      • the three suites (4,061 / 2,526 / 746) match base on the move commit and on the merge head;
      • the AST proof is OK, and its negative control fails as it should.
  8. Next: the deferred-DDL previews, priced at about −76, are the next slice.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 17:25
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 17:25
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit f1cd529 Oct 27, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-0gietk branch October 27, 2026 18:17
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