Repository navigation
Commit f1cd529
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XHoLU3ToFTmB9h8qjF9hUi5)_
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`
按需增补成员。
- **为什么改**:#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`:
- `ensureRotation`, the rotator: it inventories a rotation-managed
table's shards, adopts a legacy base table as the current shard (and
recovers when both exist), column-syncs the retained shards, drops the
expired ones and rebuilds the `UNION ALL` read view;
- `rotationShardKey` and `rotationColumnList`, which it calls (the shard
key of an instant, and the view's column list);
- `warnBuiltinColumnCollisions`, the #12015 warning the create, alter
and shard paths share when a declared field collides with a builtin
column;
- `registerExternalObject`, the DDL-free registration of a federated
object.
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
- [x] 1. No `packages/spec` change.
- [x] 2. No new name; `SchemaSyncHost` gains members only.
- [x] 3. No formatter ran; scoped `eslint --no-inline-config` passes
(repo-wide `pnpm lint` is CI's run).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. No publishable behaviour change: `skip-changeset`; both
declaration lines above.
- [x] 6. `packages/spec` not touched.
- [x] 7. No hand edits to generated docs, changelogs or release notes.
- [x] 8. Title, body and comments are in English (the maintainer brief
excepted).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5o3WH4EyHws8N24jPx)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01FEkQ5o3WH4EyHws8N24jPx
Co-authored-by: Claude <noreply@anthropic.com>
1 parent aeb9cf0 commit f1cd529
2 files changed
Lines changed: 337 additions & 201 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
86 | 107 | | |
87 | 108 | | |
88 | 109 | | |
| |||
118 | 139 | | |
119 | 140 | | |
120 | 141 | | |
121 | | - | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
122 | 146 | | |
123 | 147 | | |
124 | 148 | | |
| |||
129 | 153 | | |
130 | 154 | | |
131 | 155 | | |
| 156 | + | |
132 | 157 | | |
133 | 158 | | |
134 | 159 | | |
135 | 160 | | |
136 | 161 | | |
137 | 162 | | |
138 | 163 | | |
| 164 | + | |
| 165 | + | |
139 | 166 | | |
140 | 167 | | |
141 | 168 | | |
| 169 | + | |
| 170 | + | |
142 | 171 | | |
143 | 172 | | |
144 | 173 | | |
145 | 174 | | |
| 175 | + | |
146 | 176 | | |
147 | 177 | | |
148 | 178 | | |
149 | 179 | | |
150 | 180 | | |
151 | 181 | | |
152 | 182 | | |
| 183 | + | |
153 | 184 | | |
| 185 | + | |
154 | 186 | | |
155 | 187 | | |
| 188 | + | |
156 | 189 | | |
157 | 190 | | |
158 | 191 | | |
| 192 | + | |
159 | 193 | | |
160 | 194 | | |
161 | 195 | | |
| |||
169 | 203 | | |
170 | 204 | | |
171 | 205 | | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
172 | 211 | | |
173 | 212 | | |
174 | 213 | | |
175 | 214 | | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
176 | 218 | | |
177 | 219 | | |
178 | 220 | | |
179 | 221 | | |
180 | 222 | | |
181 | 223 | | |
182 | 224 | | |
| 225 | + | |
183 | 226 | | |
| 227 | + | |
184 | 228 | | |
185 | 229 | | |
186 | 230 | | |
| |||
1651 | 1695 | | |
1652 | 1696 | | |
1653 | 1697 | | |
| 1698 | + | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + | |
| 1702 | + | |
| 1703 | + | |
| 1704 | + | |
| 1705 | + | |
| 1706 | + | |
| 1707 | + | |
| 1708 | + | |
| 1709 | + | |
| 1710 | + | |
| 1711 | + | |
| 1712 | + | |
| 1713 | + | |
| 1714 | + | |
| 1715 | + | |
| 1716 | + | |
| 1717 | + | |
| 1718 | + | |
| 1719 | + | |
| 1720 | + | |
| 1721 | + | |
| 1722 | + | |
| 1723 | + | |
| 1724 | + | |
| 1725 | + | |
| 1726 | + | |
| 1727 | + | |
| 1728 | + | |
| 1729 | + | |
| 1730 | + | |
| 1731 | + | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
| 1755 | + | |
| 1756 | + | |
| 1757 | + | |
| 1758 | + | |
| 1759 | + | |
| 1760 | + | |
| 1761 | + | |
| 1762 | + | |
| 1763 | + | |
| 1764 | + | |
| 1765 | + | |
| 1766 | + | |
| 1767 | + | |
| 1768 | + | |
| 1769 | + | |
| 1770 | + | |
| 1771 | + | |
| 1772 | + | |
| 1773 | + | |
| 1774 | + | |
| 1775 | + | |
| 1776 | + | |
| 1777 | + | |
| 1778 | + | |
| 1779 | + | |
| 1780 | + | |
| 1781 | + | |
| 1782 | + | |
| 1783 | + | |
| 1784 | + | |
| 1785 | + | |
| 1786 | + | |
| 1787 | + | |
| 1788 | + | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
| 1793 | + | |
| 1794 | + | |
| 1795 | + | |
| 1796 | + | |
| 1797 | + | |
| 1798 | + | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
| 1803 | + | |
| 1804 | + | |
| 1805 | + | |
| 1806 | + | |
| 1807 | + | |
| 1808 | + | |
| 1809 | + | |
| 1810 | + | |
| 1811 | + | |
| 1812 | + | |
| 1813 | + | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
| 1818 | + | |
| 1819 | + | |
| 1820 | + | |
| 1821 | + | |
| 1822 | + | |
| 1823 | + | |
| 1824 | + | |
| 1825 | + | |
| 1826 | + | |
| 1827 | + | |
| 1828 | + | |
| 1829 | + | |
| 1830 | + | |
| 1831 | + | |
| 1832 | + | |
| 1833 | + | |
| 1834 | + | |
| 1835 | + | |
| 1836 | + | |
| 1837 | + | |
| 1838 | + | |
| 1839 | + | |
| 1840 | + | |
| 1841 | + | |
| 1842 | + | |
| 1843 | + | |
| 1844 | + | |
| 1845 | + | |
| 1846 | + | |
| 1847 | + | |
| 1848 | + | |
| 1849 | + | |
| 1850 | + | |
| 1851 | + | |
| 1852 | + | |
| 1853 | + | |
| 1854 | + | |
| 1855 | + | |
| 1856 | + | |
| 1857 | + | |
| 1858 | + | |
| 1859 | + | |
| 1860 | + | |
| 1861 | + | |
| 1862 | + | |
| 1863 | + | |
| 1864 | + | |
| 1865 | + | |
| 1866 | + | |
| 1867 | + | |
| 1868 | + | |
| 1869 | + | |
| 1870 | + | |
| 1871 | + | |
| 1872 | + | |
| 1873 | + | |
| 1874 | + | |
| 1875 | + | |
| 1876 | + | |
| 1877 | + | |
| 1878 | + | |
| 1879 | + | |
| 1880 | + | |
| 1881 | + | |
| 1882 | + | |
| 1883 | + | |
| 1884 | + | |
| 1885 | + | |
| 1886 | + | |
| 1887 | + | |
| 1888 | + | |
| 1889 | + | |
| 1890 | + | |
| 1891 | + | |
| 1892 | + | |
| 1893 | + | |
| 1894 | + | |
| 1895 | + | |
| 1896 | + | |
| 1897 | + | |
| 1898 | + | |
| 1899 | + | |
| 1900 | + | |
| 1901 | + | |
| 1902 | + | |
| 1903 | + | |
| 1904 | + | |
| 1905 | + | |
| 1906 | + | |
| 1907 | + | |
| 1908 | + | |
| 1909 | + | |
| 1910 | + | |
| 1911 | + | |
| 1912 | + | |
| 1913 | + | |
| 1914 | + | |
0 commit comments