Skip to content

refactor(driver-sql): put SqlDriver into facade form with the sql-driver codemod (sql-driver.ts 18,334 -> 10,979, body-identical) - #30139

Draft
objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/project-thread-y0okk4
Draft

objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/project-thread-y0okk4

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Part of #30105

维护者速读

  • 改了什么:给 scripts/codemod-split-sql-driver.mjs(Codemod split pilot: generate the sql-driver.ts split with a deterministic script, verified byte-identical #29520 的试点脚本)加上 engine.ts / protocol.ts 已有的门面层,并重新生成 sql-driver.ts。SqlDriver 成为门面:158 个方法体移入 21 个无状态 facet 类(静态方法,this 是一个具名 host),类上各留一行 .call(xHost.of(this), …) 委派;私有方法的文档块随方法体移走(21 个),公开和受保护方法的文档块留在门面;状态全部留在门面。生成文件未手改。
  • 为什么改:[Decision] Large source files: replace the hand-cut split with a script-generated split per file, so the remaining monoliths split in days, not months #29465 大文件拆分,裁决 6632500982 把门面形态(6624258595)扩展到本文件;.d.ts 门槛按 6624345766 取「公开面相等」。
  • 风险与回滚:纯搬移。158/158 个方法体逐字节等于 base,撤销拆分逐字节重建 base 的 sql-driver.ts;两次 --write 输出树 sha256 相同。公开面 .d.ts 相等(对照抓到)。driver-sql 测试 base 与 head 都是 3,859 通过;turso、sqlite-wasm 两个子类驱动计数不变。回滚即 revert。
  • 先看:① 「Facade reading」一节的 28 条例外(其中哪些是 codemod 限制、需另立卡);② codemod 的 CLASS_HEAD_SECTION 和新加的 logDurabilityFailure pin;③ check-tenant-chokepoint.mjs 新登记的 4 个模块。
  • 两分钟验证:pnpm install --frozen-lockfile 后运行 node scripts/codemod-split-sql-driver.mjs --verify --base 21510bba,预期 158/158 moved bodies identical to base、sql-driver.ts 18334 -> 10979 lines,退出码 0;--self-test 67 例通过;node scripts/check-facade-delegation-only.mjs 退出码 0。
  • 需要你做的:改动超过 3,000 行(+4,990 / −3,121,含生成文件),需您批准后落地,并在您定的窗口冻结 sql-driver.ts;本 PR 保持草稿。

Clause-②: no
Metadata-protocol: none

What changes

  • scripts/codemod-split-sql-driver.mjs gains the facade layer scripts/codemod-split-engine.mjs carries (refactor(objectql): generate the engine.ts section split with a deterministic codemod #30012, refactor(objectql): one shared host per facet in the engine.ts codemod stubs (body-identical; engine.ts 14,140 -> 13,548) #30083):
    • Each section module's moved bodies become the static methods of one stateless facet class (sql-driver-crud.ts → CrudFacet), at class indentation, unedited. Their this is one named host interface typed AS the class members (SqlDriver['x']), so a private member is reached like a public one.
    • The stub on the class keeps its modifiers and signature byte-for-byte (only async is dropped) and makes one call: return CrudFacet.x.call(crudHost.of(this), …). Each host is built once per driver by a factory the class's static block installs into sql-driver-facet-hosts.ts (types-only module). Methods go as closures called on the driver at call time, so a subclass override or an instance spy is still the one reached; getBuilder goes as a bound getter (BOUND_HOST_METHODS) so check-tenant-chokepoint still counts the binding in the moved body; fields and accessors go as getters, assigned fields with a setter.
    • Private statics the moved bodies read go through SqlDriverStatics, which a facet module imports AS SqlDriver (private-static-through-host, 6624174693).
    • Docblock ownership (6624345766 item 1): a private method's docblock moves with its body; a public or protected one stays on the facade and the facet member carries a one-line placeholder pointing back.
    • The members above the first banner form one more section, the class head (CLASS_HEAD_SECTION → sql-driver-class-head.ts). Three sections whose hand-series seam exists already take a -section suffix; sql-driver-deferred-ddl.ts and sql-driver-transactions-section.ts are new.
    • No import prune: driver-sql compiles without noUnusedLocals, so an import only a moved body reads stays, as the pilot left them.
    • The pilot's --dts byte-equality reading is dropped: a facade cannot meet it by construction (the bundled .d.ts prints every private member). The bar is now the public-surface reading below, taken outside scripts/ (6441032442 item 3).
    • parameter-shadows now fires only for a parameter named like the stub's facet class or host variable (it freed 4 methods).
    • One new PATH_READER_PINS row, logDurabilityFailure (see readers below).
    • --self-test: 67 cases in 5 pinned batteries (class-head section, the bound getter and two banner refusals added; the import-prune battery removed).
  • The generated split is --write on the unsplit sql-driver.ts of 21510bba (the parent of the pilot's landing), unedited. 158/254 methods move into 21 facet modules; sql-driver.ts goes 18,334 → 10,979 lines (12,460 on main). The leaf sql-driver-top-level.ts is byte-identical to main. index.ts re-exports none of the new modules.
  • scripts/facade-delegation-registry.json: the sql-driver.ts / SqlDriver facade row (card Codemod split: put driver-sql sql-driver.ts into facade form with a deterministic script, like protocol.ts and engine.ts #30105) and 28 exception rows, all carded refactor: move shared state out of the protocol.ts and engine.ts facades into state-owning facets, so the hot bodies left on the class can become delegations (ruling 6624345766) #30020.
  • By-path readers (6597555122 item 3):
    • check-durability-degradation-log-level judges the two MySQL widening catches that stay on the class (migrateMysqlDatetimeColumns, migrateMysqlTimeColumns) by following this.logDurabilityFailure(…) to its body in the same file. A one-call stub there cannot be followed, so both read "swallowed, no log" (measured red). The helper is pinned to stay rather than widening the gate's same-file helper walk across files; the gate's readings are then equal to base.
    • check-tenant-chokepoint's DECLARED_WATCH_HINTS names the four new sql-driver-*.ts modules (its self-test requires every scanned file to be declared).
    • Every other reader of sql-driver.ts by path (the packages/cli generate pins, check-keyed-text-bounds, sql-driver-compile-refusal-seam.test.ts, the liveness and checklist anchors, the ADR anchors) reads text the codemod keeps on the class through its existing pin and needle tables, and passes unchanged.
  • No changeset, as refactor(driver-sql): codemod pilot — generated, byte-identity-proved section split of sql-driver.ts (#29520) #29598 and refactor(objectql): generate the engine.ts section split with a deterministic codemod #30012 (both skip-changeset): no published behaviour or surface changes.

Tests

No test file changes. The suites below ran at base f68b47ca and at head; the counts are equal.

Suite Base Head
driver-sql (vitest run) 238 files passed, 12 skipped; 3,859 passed, 216 skipped the same
driver-turso (subclasses SqlDriver) 96 files; 2,496 passed, 34 skipped 96 files; 2,496 passed, 34 skipped
driver-sqlite-wasm (subclasses SqlDriver) 40 files; 750 passed 40 files; 750 passed
cli by-path pins (9 files: generate-* pins, clean.operator-text-21418) 9 files; 131 passed 9 files; 131 passed
types / metadata / metadata-protocol by-path tests, spec check-liveness.test 22 / 11 / 28 / 74 passed 22 / 11 / 28 / 74 passed

Acceptance notes

Verification

Reading Base Head
sql-driver.ts lines 18,334 (21510bba) · 12,460 (main) 10,979
Byte-identity (--write, and --verify --base 21510bba, exit 0) 158/158 bodies, 127/127 top-level declarations identical; undoing the split rebuilds the base byte-for-byte
Determinism two --write runs: output tree sha256 eab675141cad9efdb5217f65f044a246474c973777c575d9f5ee00b8c63d8154 both times; regeneration reproduces the committed tree
driver-sql typecheck exit 0 exit 0
check:facade-delegation-only (+ --self-test 36) exit 0: SqlDriver 352 members, 244 delegations, 80 fields, 28 named exceptions
check:tenant-chokepoint 24 bindings, 21 read doors, 3 insert builders, 4 tenant predicates, 0 D8 arms, 34 files the same readings, 38 files
check:durability-log-level 51 seams (9 propagating), 101 read seams (13 discriminated) the same; the output is byte-identical
pnpm build (whole repo, 119 tasks) exit 0
pnpm lint exit 0
dispatch-gates --commands (67) 67 run, every exit 0; --ran: 67 derived, 0 NOT-MEASURED, 0 UNRUN

.d.ts: ruling 6624345766 item 1, "public surface EQUAL"

Read with dts-public.mjs (project files issue-29985/dts-public.mjs, sha256 211b32062250606e658acd7fb8880e0079b4043121fb7b970c658b4b57bf5020, the reading #29985 used). driver-sql's .d.ts build is deterministic on both sides (two builds each, byte-identical), so one pair is read.

Reading index.d.ts index.d.mts
raw (sha256 base 69d9162d… / head fac28979…) differs (private members) differs
private members cut, union order, second normalisation equal (106 / 106 statements) equal
declared surface equal equal
  • The control that edits one public docblock is caught.
  • Dropping one whole private member (fileColumnsMovedDeclared with its docblock) passes.
  • The facet classes and hosts do not appear in the .d.ts.

Facade reading

Facet Members Docblocks moved
ClassHead 12 6
Lifecycle 11 6
Crud 7 2
SequenceSemantics 23 3
BulkOperations 7 0
TransactionsSection 1 0
Aggregation 3 0
Distinct 1 0
WindowFunctions 1 0
QueryPlan 2 0
SchemaSyncSection 3 0
Rotation 9 0
DeferredDdl 3 0
ManagedDrift 8 0
InternalHelpers 7 0
FilterHelpers 30 3
FieldMapping 6 0
WindowFunctionBuilder 1 0
ColumnCreation 14 1
DatabaseHelpers 7 0
SqliteSerialisation 2 0
Total 158 21

The 28 exception rows (all #30020):

Reason Members
state: constructor logic, field / static initializers holding arrows constructor, logger, static DIALECT_CONNECT_TIMEOUT
accessor (codemod moves methods only) get supports, get insertReturnsStoredRows, get dateGranularityCapabilities, get supportsWalJournal, get dialectName
a computed parameter default rotateShards, ensureRotation
path-reader logDurabilityFailure, ensureShardTable, initObjects, migrateMysqlDatetimeColumns, migrateMysqlTimeColumns, resolveWithheldFilterRefusal, createAuditTimestampColumn, varcharColumnChars, createColumn
field-call: knex listPhysicalColumns, getBuilder, explainUnkeyableTextColumn, explainHashShadowDuplicate
inferred-return-type applyFilters, applyFilterCondition, ensureDatabaseExists, createDatabase
comment-in-parameters assertOperatorAppliesToColumn

The 78 hand-series stubs stay as they were (their modules are the hand series', not facets) and read as delegations.


🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzhBZia3VxBbmZ7wCkS

…tyFailure on the class

The facade registry gains the sql-driver.ts / SqlDriver row and one
exception row per member that keeps a body (28). The codemod gains a
PATH_READER_PINS row for logDurabilityFailure: check-durability-degradation-
log-level judges the two staying MySQL widening catches by following that
helper to its body in the same file, and a one-call stub there reads as
"swallowed, no log". Regenerated: 158/254 methods move; sql-driver.ts
18,334 -> 10,979 lines.

Claude-Session: https://claude.ai/code/session_01WGufzhBZia3VxBbmZ7wCkS
Co-authored-by: Claude <noreply@anthropic.com>
…or dispatch-gates

The gate scans every sql-driver-*.ts module and its self-test requires each
scanned file to be named in DECLARED_WATCH_HINTS. The facade split adds
sql-driver-class-head.ts, sql-driver-deferred-ddl.ts,
sql-driver-facet-hosts.ts and sql-driver-transactions-section.ts. The
readings are unchanged (24 bindings, 21 read doors, 3 insert builders,
4 tenant predicates, 0 D8 arms; 34 -> 38 files).

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

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

36 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb.

⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 43 pages)
  • 16 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 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; 103 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 a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…#30147

The accessor, computed-parameter-default, path-reader, knex field-call,
inferred-return-type and comment-in-parameters rows move from #30020 to
#30147, the card filed for them. The three state-owning rows
(constructor, logger, static DIALECT_CONNECT_TIMEOUT) stay on #30020.

Claude-Session: https://claude.ai/code/session_01WGufzhBZia3VxBbmZ7wCkS
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · os-support-ai · session_01WGufzgBDcBNVmSmo7VFHpj

Stamp: 2026-11-04T16:28Z
Head-sha: 61c98d1

维护者速读

席位核对

This branch has not been deployed

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

Labels

needs-user-decision size/xl 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.

1 participant