feat(spec,objectql,driver-sql,driver-turso): a transport can declare it has no transactions, and the engine gates on the declaration - #18890
Conversation
…it has no transactions, and the engine gates on the declaration WIP — implementation in place, verification pending. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…ee layers, plus the changeset Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…te the three stale spec artifacts Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…clare-no-transactions
…igins from the merged tree Merge of origin/main routed three os-regen artifacts without a text merge. Regenerated after the merge commit, never during it. Asserted: zero lines present in origin/main are absent from the regenerated files, and the only addition is this branch's own new export. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 5 package(s): 8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin de136d65ba30f396dbf6d43bc176fb9ea28aac89 && git checkout de136d65ba30f396dbf6d43bc176fb9ea28aac89
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbd474431fdf39a2a3d31f3f6f0475eb51fa70f7 e53dc612b4822691edd263655ecb264312bd55f0 && git checkout -B drift-repro dbd474431fdf39a2a3d31f3f6f0475eb51fa70f7 && git merge --no-ff e53dc612b4822691edd263655ecb264312bd55f0
node scripts/docs-audit/affected-docs.mjs --json dbd474431fdf39a2a3d31f3f6f0475eb51fa70f7
|
Contract reviewServed-tier: 109/109 ⭐ Tier verified by the seat from the reviewing round's harness-stamped per-message served-model field: 109 of 109 at tier, 0 off-tier. ① Derived judgmentsWhat is sound, and is ⛔ not reopened. Clause 3 landed: Seat decision 1 (spelling (c)) — upheld, and independently re-derived. ⭐ Tombstones undisturbed — ⛔ The finding this round FAILS onClause 1 is short by ONE gate, and that re-opens the defect this card exists to close.
⭐ The seat re-read that line itself and confirms it. Its own docblock calls it 「the ADR-0119 D4 gate, shared」; it is exported from ⇒ the re-keying made the two gates disagree. Before this PR both read presence, they agreed, and the driver's 501 surfaced. Now Measured, with a lit control — resolution path established first (neither
Also measured against the real ⇒ on two reachable paths — the per-object REST batch with
② Semver level
③ Boundary flags⭐ One the seat must own. The diff removes three public members of the exported Clause ②
NOT MEASUREDThe dev's two ablation legs were not re-run (the resolution-path premise was confirmed instead and the declared-vs-undeclared columns exercise the dist predicate end to end, but no source mutation was performed). Local Implemented-by: VERDICT: FAIL Generated by Claude Code |
…ethod presence (#18063) The fourth gate. `ObjectQL.transaction()`, `ScopedContext.transaction` and the `ScopedContext` begin/commit/rollback trio were re-keyed onto `driverSupportsTransactions()`; `engineCanRollBack` was not, so the two disagreed for exactly the driver this card exists for — a transport that inherits `beginTransaction` from a base class it cannot honour and declares `supports.transactionsUnsupported`. Measured on the real chain (real `ObjectQL`, real `engineCanRollBack` through core's `dist`, real `ObjectStackProtocolImplementation.batchData`, real `runMigrationJournal`), on a declaring driver, before this commit: - `batchData({ atomic: true })` answered `succeeded: 0, failed: 2` — a rollback — with `begins = 0` and record 0 still on disk. The lit control, the same double with the bit removed, opened one transaction and left zero rows behind. - `runMigrationJournal` did not refuse: it ran to `completed` with `begins = 0` and wrote `chunk_started`, `chunk_done`, `run_done` — the `done` marker its own header says would not mean committed. So the degrade swallowed the refusal the base tree produced, one layer up from the engine. `engineCanRollBack`'s driver clause now asks the same predicate the engine dispatches on, which is what "shared so the two cannot drift" was for. `FakeEngine` gains the shape that makes the existing pin able to fail: its driver published `beginTransaction` and carried no `supports` record at all, so a gate reading presence and a gate reading the declaration were indistinguishable against it. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…clare-no-transactions
…e now runs
The retired-key prescription still opened with "Transaction use is gated on
METHOD PRESENCE", which this branch made false: `engine.transaction()` asks
`driverSupportsTransactions(driver)` — the method present AND
`transactionsUnsupported` not declared. The paragraph appended further down
already drew the distinction correctly, so the opening clause was the one
sentence in the prescription that contradicted the code around it, and an
author who writes the retired key reads that sentence first.
The opening clause now names the real gate and keeps method presence as the
clause it still is rather than the whole test. Nothing else in the
prescription moves: the Discovery `transactionalBatch` sentence, the
"NOT this key restored" refusal text and "A driver with real transactions
declares nothing" are untouched, `savepoints` and `isolationLevels` are
untouched, and the retired-bit roster is unchanged.
`content/docs/references/data/driver{,-sql,-nosql}.mdx` are the generated
projection of that string — regenerated by `check:generated --fix`, which
proved `gen:docs` the only stale artifact in the tree and reproduced every
other reference page byte-identically.
Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho
Co-authored-by: Claude <noreply@anthropic.com>
✅ 达档合约复核 PASS —— head
|
| 取法 | 读数 |
|---|---|
| 源码 | migration-journal.ts:105-121 的 driver 分支已改成 driverSupportsTransactions(...),从 @objectstack/spec/data 导入 |
| 消融 | 把该分支改回 typeof ….beginTransaction === 'function' ⇒ core 的 migration-journal.test.ts 2 失败 / 21 通过,失败的恰是两条 [#18063];还原后 blob 与 HEAD 相同、porcelain 空;未消融时 23/23 |
| 真链路 | 真 TursoDriver 远端实例 → transactionsUnsupported=true → engineCanRollBack=false;engine.transaction(cb) 跑了回调、没抛、告警一次;{require:true} 抛 TransactionUnsupportedError。亮对照:同一探针对 :memory: 本地实例 ⇒ 位 false、谓词 true、engineCanRollBack=true |
⇒ 声明式驱动不再从这道闸漏过去。
⭐ 它自己避开了本席刚踩到的那个坑
条款②读数取的是被复核那棵树上的匹配器(check-widening-tells.mjs blob 31cb9de0…),并明说 origin/main 上的 032bd9db… 没有使用。⇒ 这正是本席在 #18638 上刚栽过的「旧匹配器读数」陷阱,它没栽。
分级:minor 是裁出来的,不是推出来的 —— 记录里明写
RemoteTransport 去掉三个公开成员,严格 semver 读作 major。裁决第 5 条按名点了这次移除并定 minor,理由是远端 transport 从来没有兑现过事务 —— 复核证了这条理由:main 上该驱动自 62bce5c297d 起就以 501 拒绝。复核原话:「The level is RULED, not derived, and this record says so knowingly.」
⭐ 本席采信这种写法,并要它成为惯例:裁出来的分级必须自报是裁出来的,⛔ 不许伪装成推导结论。
条款②:yes (widening) —— 新增可写拼法 + 新公开导出。卡上载体是无臂的 Clause-②: yes,一致,⛔ 无需重新声明。(narrowing) 不得声明:它会读作 BREAKING,与裁出来的 minor 打架。
本席欠的两张卡(③.1 / ③.2),另开,⛔ 不挡本卡
- 墓碑钉子对自己的主题已瞎(test-only,一行正则 + 标题)。
- Discovery 的
transactionalBatch仍只从引擎推(protocol.ts:6333),于是声明了该位的组合照样广告transactionalBatch: true,而batchData({atomic:true})答 501 —— 而那条 501 的补救文案偏偏叫人「先探capabilities.transactionalBatch」。⭐ 无方法那一半在 main 上本就如此,本 PR 只是给同一处盲点添了第二个人群,且墓碑行文如实写了。归domain:engine。
以下为达档复核记录原文,本席逐字采纳,⛔ 未编辑一字。
Contract review
Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ddaffccce86b5deb8fe075f5f50df8e6088120fc
Isolated contract-review subagent for the domain:spec seat. PR #18890, card #18063, branch claude/issue-18063-declare-no-transactions. Every reading below was taken by this review against the head above (worktree detached at it at /tmp/claude-0/-home-user/d31c56ec-952d-5a16-945f-6e430b9a6d34/scratchpad/pr-18890/wt, deps installed, spec/core/objectql/driver-turso closures built with .d.ts under the shared lock: 15/15 tasks, exit 0), or against origin/main at the sha named in the line. Exit codes captured before any pipe. Clone deepened (--deepen=400) before any ancestry negative was trusted; all five cited commits resolve and 873e0e8e270 is an ancestor of both head and origin/main.
① Derived judgments
Claim 1 — the merge (33844063be4) was clean and neither MIXED file needed hand resolution: CONFIRMED from the tree, not the report. Parents e3ad06ec35b (branch) + 873e0e8e270 (main); first-parent diff 95 files. packages/spec/dropped-refinements.baseline.json = blob 6622c96… and packages/spec/src/migrations/registry.ts = blob b2d7a0d… at head, at the main-side parent and at today's origin/main alike; git diff 873e0e8e270...HEAD -- (both paths) empty (control: driver.zod.ts on the same command shows 103/6). Independent of any driver: a bare --shared probe clone with no merge.os-regen.driver registered ran merge-tree --write-tree 873e0e8e270 e3ad06ec35b → exit 0 (clean) and its tree 2d66651333e1cf22915bb2cb81da049b95071fae is byte-identical to the merge commit's tree. The routed artifacts that moved on main between the two merge bases were api-surface/api.json, authorable-surface/{api,ui}.json, export-origins/api.json (8 insertions); the branch touched only the data.json shards — textually disjoint, so the driver had nothing to route. The os-regen-pending argument is not needed and not relied on.
Claim 2 — nothing else moved: CONFIRMED. git diff --numstat 0b31d90fb37 e3ad06ec35b and git diff --numstat 873e0e8e270...HEAD name the same 20 files; diff of the two listings is exactly one line, driver.zod.ts 97/3 → 103/6. The same 20-file listing is also identical against origin/main...HEAD at the time of reading (three-dot; origin/main had moved to d8b12fca97c, merge base still 873e0e8e270).
Claim 3 — the tombstone prose fix and its projection: CONFIRMED. The final commit ddaffccce touches exactly four files: driver.zod.ts (+6/−3 inside the transactions retiredKey string) and the three content/docs/references/data/driver{,-sql,-nosql}.mdx rows that carry that string (4 hunks, the transactions row only). Generated, not hand-edited: at this head check:generated (no --fix) reports check:docs content/docs/references/** ✓, so the three rows are byte-identical to what gen:docs produces from the edited string.
Claim 4 — is the new prose TRUE, or shaped for the regex? TRUE, and it is the truth the runtime now has; the regex is the part that no longer proves it.
The new opening reads: 「Transaction use is gated on the DRIVER'S DECLARATION, no longer on METHOD PRESENCE alone: engine.transaction() asks driverSupportsTransactions(driver) — driver.beginTransaction present AND transactionsUnsupported not set (ADR-0034 ambient transactions, ADR-0119 D1). A driver without the method — or a transport that declares that live bit — gets the non-transactional fallback, whatever this bit claimed.」
Checked clause by clause against the code at head: driverSupportsTransactions (driver.zod.ts:846-857) is exactly typeof beginTransaction === 'function' AND supports.transactionsUnsupported !== true; engine.transaction() calls it at engine.ts:14901; ScopedContext.transaction at :15791; txDriver() at :15885. "No longer on METHOD PRESENCE alone" is a correct description of a conjunction whose first conjunct is still presence — it is not a sentence one would write differently without the regex, and it is not shaped around it: the fact it states is the fact the pin would need to assert. What the regex adds is nothing: it matches the new text (true) AND, by my control, it also matches the OLD false sentence (true) — so driver.test.ts:124-127 no longer discriminates the true opening from the false one. That is a pin-narrowness finding (③ below), not a prose finding. The two ADR citations hold: ADR-0119 D1 is where the "callback runs without a transaction and without rollback" degrade is declared (docs/adr/0119…:90).
The fourth gate — engineCanRollBack, packages/core/src/utils/migration-journal.ts — NOW HONOURS THE DECLARATION. Measured three ways, not read off a green file.
- Source at head (
:105-121): the driver clause is!defaultDriver || driverSupportsTransactions(defaultDriver as {...}), imported from@objectstack/spec/data(:58). Repo-wide grep for presence-shaped reads (typeof …beginTransaction,?.beginTransactiongates,'beginTransaction' in) across all ofpackages/, non-test, non-dist: the predicate's own body, two comments, andquickjs-runner.ts:621— which reads the engine-API trio'sbeginTransaction(whosebeginnow returnsnullfor a declaring driver, and the runner already degrades onnull). Control lit:driverSupportsTransactionshas 5 non-test call/import sites. - Ablation at head: with the driver clause mutated back to
typeof (defaultDriver …).beginTransaction === 'function'(anchor unique, marker seen on disk, blobdf8d8d5…→e670282…), core'smigration-journal.test.tsgoes 2 failed / 21 passed — exactly the two[#18063]cases; restored blob identical to HEAD, porcelain 0; unmutated run 23/23. - Real chain through built
dist: a realTursoDriver({url:'libsql://…'})(remote) →supports.transactionsUnsupported = true,driverSupportsTransactions = false, and a realObjectQLwith it registered as default →engineCanRollBack = false;engine.transaction(cb)ran the callback, threw nothing, warned once naming the declaration;{require:true}threwTransactionUnsupportedError. Lit control, same probe onTursoDriver({url:':memory:'})(local): bitfalse, predicatetrue,engineCanRollBack = true. Consumers of the gate at head:metadata-protocol/protocol.ts:12315(atomic batch → 501NOT_IMPLEMENTEDon false) andrunMigrationJournal(:473, refusesNOT_IMPLEMENTEDbefore any journal row). A declaring driver no longer slips through.
Claim 5 — check:generated on the merged tree: CONFIRMED at this head. pnpm --filter @objectstack/spec check:generated → all 15 gates ✓, 「All 15 generated artifacts are up to date」, exit 0 (lock wrapper VERDICT command-exit 0). 15 is this head's count: PR #18971 (origin/main d8b12fca97c, after this head's base) adds a new artifact family this tree does not carry — ③.4. The 「exactly one stale artifact before --fix」 half is not re-measurable from a committed head; what is measurable is that the final commit's footprint is exactly the string plus its three projections, which is the footprint of gen:docs alone being stale.
Claim 6 — tests: CONFIRMED where re-run. core migration-journal 23 passed; objectql engine-transaction-declared-unsupported + protocol-batch-atomic 15 passed / 2 files; driver-turso turso-transactions-unsupported-declaration + turso-driver-doors-declared-types + turso-driver 112 passed / 3 files; spec pin by name (-t "transactions prescription") 1 passed | 57 skipped, and the pin file is untouched by the prose commit (git diff 33844063be4 ddaffccce -- driver.test.ts empty). spec test 489 files / 14213 tests, exit 0; spec typecheck exit 0 (tsc --noEmit + scripts + test-layer typecheck) — both under the shared lock on this worktree, exits captured per part before any pipe. CI at this head, read from the check runs: all 33 completed runs success, 2 skipped (Console Pin Gate, Packed-tarball smoke — expected skips); Lint & Repo Gates completed success at 10:08:00Z.
Other derived judgments on the diff. DriverCapabilities gains one optional key, absence = false (parse test asserts absence; my control {transactionsUnsupported: undefined} → true). Retired roster unchanged: RETIRED_BITS 31, LIVE_BITS 4; the transactions/savepoints/isolationLevels tombstones still refuse. IDataDriver.beginTransaction TSDoc gains a paragraph, signature unchanged. SqlDriver.supports spells transactionsUnsupported: false; SqlDriver.beginTransaction untouched. TursoDriver.beginTransaction() republishes ReturnType[SqlDriver['beginTransaction']] (bracket-free spelling here; the source uses the generic form) — the any is gone. RemoteTransport loses beginTransaction/commit/rollback — it IS publicly exported (driver-turso/src/index.ts:38); in-repo callers 0 (control lit), pinned objectui 53ded82bf: RemoteTransport 0, driver-turso 0, beginTransaction 0 (control: @objectstack/spec 2252 lines / 825 files). The driver-level refusal 62bce5c297d is an ancestor of head and of origin/main; refuseRemoteTransaction is live at head (8 occurrences vs 6 at base). ADR anchors exist for engine.ts and sql-driver.ts; check:adr-anchors is inside the green Lint & Repo Gates.
② Semver level
minor on all five (spec, core, objectql, driver-sql, driver-turso), and the changeset at head declares exactly that. New optional key + new export + engine dispatch = additive; absence = false keeps every existing driver's behaviour. One hunk is narrowing-shaped: three public members removed from the exported RemoteTransport. Strict semver reads that as major; ruling clause 5 orders the removal by name and sets minor on the stated ground (the remote transport never honoured a transaction, so no working behaviour is withdrawn — confirmed: on main the driver already refuses them with 501 since 62bce5c297d). The level is RULED, not derived, and this record says so knowingly. The changeset is not breaking, so the ADR-0087 disposition marker is not required (check-adr-0087-registration keys off a declared breaking/major changeset); it carries the migration-shaped prose anyway. Check Changeset green. No CHANGELOG.md, no content/docs/releases/ touched.
Clause ②: yes (widening). New authorable spelling on a published record (T1 at driver.zod.ts:270) and a new public export (T3 at api-surface/data.json:735) — read with the widening-tells matcher from the reviewed tree (scripts/pm/check-widening-tells.mjs blob 31cb9de0… at ddaffccce; the shared checkout's 025f8e51… gave the same two tells; origin/main's 032bd9db… was not used). The card's carrier reads Clause-②: yes with no arm (check-clause2-carriers --pair 18890 → exit 0, both carriers agree, correction comment 5725140321 governs) — consistent; no re-declaration needed. (narrowing) is not declared and must not be: it would read BREAKING against the ruled minor; the narrowing-shaped hunk is the ruled exception above. This is 已发布契约面 widened, not 拉回已声明契约.
③ Boundary flags — each its own card, none blocks
- The tombstone pin is now blind to its own subject (test-only pin tightening).
driver.test.ts:124-127's regex…removed.*METHOD PRESENCE.*beginTransaction.*Delete the keymatches the new true prose AND the old false one (measured). Its title 「points at method presence」 is narrow, not wrong: presence is still the first conjunct. Verdict on the seat's question: nothing in this PR; a follow-up card. The dispatch fenced the pins, the prose is true independent of the pin, and the fix is a one-line regex + title rename (…removed.*DECLARATION.*driverSupportsTransactions.*beginTransaction.*Delete the key). Does not block: a pin that under-asserts a true sentence is coverage debt, not a contract defect; test-only pins arepm:queue-eligible. - Discovery's
transactionalBatchstill derives from the engine alone (metadata-protocol/protocol.ts:6333:typeof this.engine?.transaction === 'function'), so a composition whose default driver declares the bit — or has nobeginTransactionat all — advertisestransactionalBatch: truewhilebatchData({atomic:true})answers 501, and the 501's own remedy text says 「probe capabilities.transactionalBatch on /discovery first」. Pre-existing for the no-method case onmain; this PR adds a second population to the same blindness and its tombstone prose says so truthfully (「never from this bit」). Remedy is one line: readengineCanRollBack(this.engine)there. Does not block: the ruling's scope is the engine's gating, discovery was already blind before this head, and Route & surface ownership §4 makes it a finding class (b) card, filed indomain:engine/metadata-protocol. DriverConfig.capabilitiesas an AUTHORED record has no runtime reader in objectql/core/runtime/metadata-protocol (grep, control: 4driver.supportsreaders). The runtime reads the driver INSTANCE'ssupports. Pre-existing and shared withautonumber/batchSchemaSync(rows present inauthorable-surface/data.jsonat base, lines 233-234/266); the liveness ledger does not coverDriverCapabilities(not a registered metadata type; control:queryDateGranularityabsent from every ledger file too). Observation for the seat; not this PR's.- Landing-time gate from PR feat(spec): pin every export by its .d.ts declaration text, and retire the 27 signature hashes #18971 (
origin/maind8b12fca97c, after this head's base): the newpackages/spec/api-surface-declarations/*.txtshards are absent from this tree by construction; the seat's own reading says this PR will red that gate until regenerated on a merged tree. Not a review finding; the seat's merge-and-regenerate round. Knowing this changes nothing in the verdict: every conclusion above is anchored to873e0e8e270as base and says so where it matters (Claims 1, 2, 5).
NOT MEASURED
- The whole-package suites for driver-sql (2627), driver-turso (1282), objectql, core — CI's (
Test Core6/6 green at this head); only the named files above were re-run here. - The dev's two ablation legs (spec predicate deleted from
dist; turso declaration flipped) — not re-run; my ablation was on the fourth gate only, and my real-chain probe exercised the built spec predicate end to end. - A booted REST server against a live Turso remote datasource — the probe used a real
TursoDriverremote instance and a realObjectQLthroughdist, without REST, without network. objectstack-ai/cloudconsumers of the removedRemoteTransportmembers — outside the readable repositories.- [finding] Nine more IDataDriver doors are still masked as
anyon the SQL drivers — the #17277 census answered triage's 「is aggregate the only one?」 with NO #17690 / fix(driver-sql,driver-turso): eight more IDataDriver doors publish their declared return type #17876 / [finding] TursoDriver.beginTransaction 的 any 掩盖的是 Liskov 违例,不是未收窄的门 —— remote 模式返回 libsql 事务,而继承来的声明承诺 Knex.Transaction #17878 original text — still 404 at review time (control: [Decision] TursoDriver.beginTransaction 的 any 掩盖的是 Liskov 违例 —— 基类声明该服从谁,A 还是 C(重建自 #17878) #18063 read fine through the same instrument this round); the ruling's restatement governs. - Whether
check:generatedat a tree that includes feat(spec): pin every export by its .d.ts declaration text, and retire the 27 signature hashes #18971's new artifact family passes — my head predates it; the count reported above is what this head has. check:type-check-debtlocally — CI'sType Check · debt ledgergreen.
Implemented-by: claude/issue-18063-declare-no-transactions
Reviewed-by: session_01LvwGppdonww4zGLWZo5rho
VERDICT: PASS — landing head ddaffccce86b5deb8fe075f5f50df8e6088120fc.
Scratch (not part of the record): review worktree left at /tmp/claude-0/-home-user/d31c56ec-952d-5a16-945f-6e430b9a6d34/scratchpad/pr-18890/wt (detached at the head, porcelain clean after the ablation restore); logs and the ablation/probe scripts beside it under …/scratchpad/pr-18890/. No GitHub writes were made. The refs/pm-review/pr-18890-head ref in the shared clone points at the landing head.
Generated by Claude Code
…clare-no-transactions
`main` grew `packages/spec/api-surface-declarations/` after this branch's base, and this branch's two additions to the `./data` entry are absent from those shards: the optional `supports.transactionsUnsupported` capability bit and the `driverSupportsTransactions` predicate. So the family read stale the moment the merge landed, which is what dequeued the sibling PR from the merge queue. Regenerated from a real build with the command the queue build printed: `pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec gen:api-surface-declarations`. No shard was hand-edited. Two shards move and both moves are additive. `data.txt` gains `transactionsUnsupported: z.ZodOptional<z.ZodBoolean>` on the five schemas that embed the driver capabilities object and the `driverSupportsTransactions` declaration, taking the entry from 832 exported names / 845 declarations to 833 / 846. `contracts.txt` gains the `beginTransaction` TSDoc paragraph on `IDataDriver` that says method presence is not a transaction claim. Nothing is removed: `check:api-surface` reads "public API surface unchanged" both before and after the regeneration, so the exported-name set is untouched and only declaration text moved. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Isolated contract-review subagent for the ① Derived judgments — the seven claims, each against my own instrumentClaim 1 — merge via
Claim 2 — routing read from the MERGED tree: CONFIRMED. Claim 3 — the two MIXED files are NOT routed and never conflicted: CONFIRMED. Claim 4 — two shards moved, BOTH moves additive, no shard hand-edited: CONFIRMED, at the line level, at the declaration level, and by byte-identical regeneration.
Claim 5 — Claim 6 — 22 files = the prior 20 + the 2 shards: CONFIRMED. Claim 7 — the mutation probe: REPRODUCED, and it proves what it claims — with one limit the round did not state, which I closed with a second probe. Probe A (as the round did): ⭐ The seat's ruling A (comment
|
| token | packages/spec/api-surface/ |
packages/spec/api-surface-declarations/ |
|---|---|---|
RemoteTransport |
0 files | 0 files |
lit DriverCapabilities |
2 files (contracts.json 1, data.json 2) | 2 files (contracts.txt 5, data.txt 4) |
lit beginTransaction |
0 files — lit control on this dir: IDataDriver 1 file; the rows are name (kind) only, so a member name cannot appear here by construction |
2 files (contracts.txt 3, data.txt 2) |
lit driverSupportsTransactions |
1 file | 2 files |
dark ZzQxNonsenseTokenQxZz |
0 | 0 |
RemoteTransport is declared once, at packages/drivers/driver-turso/src/remote-transport.ts:1104, exported from driver-turso/src/index.ts:38. Both spec artifacts are built by collectEntries() from packages/spec/package.json's 17 typed exports entries — driver-turso is not an input to either gate by construction, and RemoteTransport's only appearances under packages/spec/ are CHANGELOG, spec-changes.json, migration entries and comments, none under api-surface*/. The three members are present at 873e0e8e270 (remote-transport.ts:1845/1850/1854) and absent at ddaffccce86b and 4d118176b7f; git diff --stat ddaffccce86b 4d118176b7f -- packages/drivers/driver-turso/src/ is EMPTY — the removal predates this delta and the delta does not touch it (main's only driver-turso change in the window is a README fetch typing example, lines 171–175, unrelated). ⇒ The dispatch's STOP clause named an instrument that (a) cannot see a driver-turso removal at all and (b) does not even own the words REMOVED/RESHAPED (those are the declarations gate's). The pre-regeneration red the declarations gate DID report is exactly probe B's seven rows — all insertions of an optional key, a new function, or comment text — this PR's own additive change, already judged 「扩大公开面」 by the prior PASS. Nothing in this delta touches the ruled minor; A (accept, do not re-rule) is the correct ruling. One reading to keep beside it: the declarations gate's advisory 「A REMOVED or RESHAPED declaration is a breaking change … rule minor/major」 fires on ANY text change including a comment insertion — "reshaped" in that gate's vocabulary is a prompt to read the diff, not a narrowing finding.
② Semver level and Clause ② for the delta
The changeset at head (.changeset/18063-transport-declares-no-transactions.md, unchanged by the delta) declares minor on all five packages; the delta adds no package, no export, no key and no closed-set member beyond what the prior PASS judged, so minor stands, and the ruled exception (RemoteTransport removal at minor by ruling clause 5) is untouched — see above.
Clause ② for the delta: yes, by restatement only. SKILL.md:515 「条款②只指已发布契约面,拉回已声明契约不触它;卡面复述仍是条款②」 — the two regenerated shards are the shape-half projection of the same published-contract widening (new optional key on a published record, new public export) that the standing declaration already covers; they widen nothing further and narrow nothing. The declaration therefore needs no change: PR body and card must remain yes, and ⛔ (narrowing) must not be declared (it would read BREAKING against the ruled minor). Clause-②: yes (line 3), not yes (widening); yes (widening) is the direction the prior review record derived, not text on the body. The card is bare yes via correction 5725140321 (which the carriers reader selects, attributed to session_01LvwGppdonww4zGLWZo5rho). node scripts/pm/check-clause2-carriers.mjs --pair 18890 at the landing head, from the reviewed tree (script blob 3a270ef2eb5…), exit 0: both carriers readable and agreeing, thread complete (15 card comments / 3 PR comments, ladder stopped on a short page). Bare yes is a well-formed spelling (AGENTS.md Post-Task §3: yes|no plus at most one arm), so this is consistent, not a defect; adding (widening) to the body would be optional and harmless.
③ Does the delta change any conclusion of the prior PASS? NO.
None of the 20 PR files moved (Claim 6); origin/main in the window touched nothing under packages/spec/src, packages/objectql, packages/core, packages/metadata-protocol or packages/drivers/*/src (only READMEs, plugin-hono-server, packages/lint, docs and #18971's spec tooling); the two MIXED files are byte-stable on every side (Claim 3); every gate the prior record cited is green again on the merged tree; the prior record's boundary flag ③.4 (landing-time gate from #18971) is exactly what this delta discharges, and its check:generated count 15 is superseded by 16 as that flag predicted. The prior PASS's ①/②/③ stand unchanged.
Boundary flags — each with why it does not block
- PR body reads bare
Clause-②: yes, notyes (widening)as the brief states. Not blocking: valid spelling, both carriers agree, carriers predicate exit 0,Check Changesetsuccess at head; the arm is review-derived. - Declarations-gate vocabulary trap: "reshaped" = any declaration-text change (a comment insertion counts). Not blocking: probe B's seven rows were read one by one and are all widenings; the ruling reads the diff, not the word.
check:generatedtruncates a failing gate's list to three lines — reproduced in probe B (contracts.txtand fourdata.txtrows cut). Not blocking: the standalone gate carries the inventory, which is what this review and the round used.packages/drivers/driver-turso/README.md:25still listsRemoteTransport: beginTransaction, commit, rollback— present at873e0e8e270, untouched by branch and by main, so it has been stale since the members were removed at the prior head, ⛔ not a delta change; hand-written (no generator names the file). Not blocking: outside this delta's scope and not a contract artifact; a docs-accuracy follow-up (acceptance note or docs-only PR) for the seat.- A member TSDoc edit reaches the "shape" artifact (
contracts.txt+9 comment lines insideIDataDriver) despite the header's "leading TSDoc excluded" — instrument behaviour (getText()of the enclosing interface), not drift. Not blocking; a reader note for the next review that sees a comment-only "reshape". - Shallow clone (
.git/shallowpresent, 423 commits): all six named commits resolve,merge-baseanswered; no ancestry negative was relied on. Not blocking. os-regen-pendingabsence was not read from the round's$GIT_DIR(its worktree is not mine to read); it is established mechanically — empty both-sides overlap plus a byte-identical driverless merge tree — which is stronger than a transcript line. Not blocking.
NOT MEASURED
- The round's
os-regen-merge.shtranscript itself (its KEEPING lines, the marker file) and itscheck:generated --fixpass — replaced by tree-identity and byte-identical regeneration, not re-run. - CI convergence at
4d118176b7f: at 2026-09-18T11:53ZLint & Repo GatesandTest Core (1/6, 5/6, 6/6)werein_progress;TypeScript Type Check,Type Check · consumer gates(the job that runscheck:api-surfaceandcheck:api-surface-declarations, success 11:46:42Z),Type Check · workspace,Type Check · debt ledger,Build Core,Governed Surface Queue Guard,Check Changeset,Temporal Conformance,Dogfood Regression Gateweresuccess;Console Pin GateandPacked-tarball smokeskipped(expected). The seat's landing check ③ (every check success) is the seat's to read after convergence. - Whole-package test suites and
pnpm typecheckat the landing head — no code file moved in the delta; CI's. - The merge queue's server-side rebuild of this head (the only instrument for that question).
objectstack-ai/cloudconsumers and the objectui pin grep forRemoteTransport— unchanged from the prior record, not re-run.- This review's own served tier — not self-attestable; the seat reads it from the harness stamps.
Implemented-by: claude/issue-18063-declare-no-transactions
Reviewed-by: session_01LvwGppdonww4zGLWZo5rho
Scratch (not part of the record): review worktree at /tmp/claude-0/-home-user/d31c56ec-952d-5a16-945f-6e430b9a6d34/scratchpad/pr-18890/r2/wt (detached at the landing head, porcelain 0 after both probe restores); driverless probe clone …/pr-18890/r2/PROBE.git; gate logs …/pr-18890/r2/{gates.out,1-*.log,2-*.log,3-*.log,4-*.log,5-*.log,clause2-pair.log,build.log,install.log}. No GitHub writes were made.
VERDICT: PASS — landing head 4d118176b7f9169f355fb142c01dacb0e977dce0.
Generated by Claude Code
✅ 增量复核 PASS —— 落地 head
|
…clare-no-transactions
The os-regen driver deferred on packages/spec/api-surface-declarations/data.txt (both sides changed it), so the merge commit carried this branch's side and silently dropped main's. Step 2 restored main's side into the worktree and this regeneration re-derives the branch's declarations on top, so the shard now holds both: this branch's `transactionsUnsupported` / `driverSupportsTransactions` declarations and main's `ResolveApiOptions.userExportAllowed` doc block. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Isolated contract-review subagent for the Instrument blobs named ( ① Derived judgments — the five claims, each against my own instrumentClaim 1 — the driver fired and deferred on
Claim 2 — both-sides survival at the head: CONFIRMED, re-measured with my own controls, and the round's numbers reproduce cell for cell.
Claim 3 — gates: PARTIALLY verified; the local re-runs did not complete before hand-back and are listed under NOT MEASURED, ⛔ not adopted. What was read: fresh worktree at Claim 4 — six files byte-equal to main at the head: CONFIRMED by blob id, with a lit control. Claim 5 — the prescribed lit control did not light, and the synthetic substitute: JUDGED ADEQUATE for what that probe can attest, with one scope caveat the seat should carry forward. Reproduced: the driver-free Clause-② carriers: ② Semver level and Clause ② for the delta
③ Does the delta change any conclusion of the two prior PASSes? NO.The both-sides set is exactly Boundary flags — each with why it does not block
NOT MEASURED
Implemented-by: Scratch (not part of the record): review worktree VERDICT: PASS — landing head Generated by Claude Code |
✅ 增量复核 PASS —— 落地 head
|
📌 复核记录升级 —— 同一裁断、同一 head,Claim 3 从「部分」变为「实测」
复核席在交接后把它自己锁下的重活跑完了并重发了记录。裁断行与落地 head 逐字未变(本席 diff 过: ⛔ 本席不贴第二份 16KB 的近似重复 —— 已贴的 Claim 3:
|
| 运行 | 读数 |
|---|---|
turbo run build --force(spec/core/objectql/driver-sql/driver-turso 闭包) |
Tasks: 15 successful, 15 total,VERDICT command-exit 0,持锁 271s |
check:generated |
「Checking 16 generated artifacts … ✓ All 16 generated artifacts are up to date.」,VERDICT command-exit 0,持锁 79s |
pnpm --filter @objectstack/spec test |
Test Files 491 passed (491) / Tests 14299 passed (14299),VERDICT command-exit 0,持锁 189s |
⇒ 上一版 NOT MEASURED 里的两条(本地 check:generated 与 spec test)已被它自己测掉。typecheck 与六条点名门禁仍排在同一把锁后面,仍列为 NOT MEASURED —— ⛔ 它没有把没跑的说成跑了,本席也不替它圆。
本席独立读它留下的 .exit 文件,与上表一致:build.exit=0、checkgen.exit=0、spectest.exit=0。
⛔ 仍然归席位的那一条,本席照担
落地前置 ③(每一项 check 都 success)在 CI 收敛后由本席自读 —— 复核明写这条归席位,并在它的读数时点(13:41Z)还有 Test Core 1/3/5/6、Type Check · workspace、Lint & Repo Gates 在跑。本席已挂「全绿才入队」的守卫,⛔ 未收敛不入队。
Generated by Claude Code
⛔ 更正本席上一条增补(
|
| 文件 | 值 | 对应日志尾行(本席自读) |
|---|---|---|
build.exit |
0 | — |
checkgen.exit |
0 | — |
spectest.exit |
0 | — |
typecheck.exit |
0 | Tasks: 18 successful, 18 total |
gate-core.exit |
0 | Test Files 1 passed (1) / Tests 23 passed (23) |
gate-objectql.exit |
0 | Test Files 2 passed (2) / Tests 15 passed (15) |
gate-turso.exit |
0 | Test Files 3 passed (3) / Tests 112 passed (112) |
ALL-DONE 存在,验证锁已释放、队列为空。
⇒ 七项全部 exit 0,数字与复核记录相符。本席上一条把它们说成「仍未测」是读得太早,⛔ 不是复核漏测。
⛔ 仍然归席位、仍然没有被这条更正吃掉的一条
落地前置 ③(每一项 check 都 success)是 CI 的读数,不是本地门禁的读数。 本地七项全绿不能替代它 —— 本席的入队守卫仍按 CI 判,在 37 绿 0 红 1 在跑 上拒绝过三轮,直到全绿才放行—— added_to_merge_queue 事件,2026-09-18T13:53:56Z。
⭐ 复核在这一点上也没含糊:它把 NOT MEASURED 一路收窄到只剩真正不可测的几项(队列自己的服务端重建、它自己的服务档位),而不是收窄到零。
Generated by Claude Code
…ed on the origin/main merge The merge of origin/main (b146102) routed four paths to the os-regen driver, which defers rather than text-merging: the three api-surface-declarations shards automation.txt, data.txt and ui.txt, and the generated reference page content/docs/references/ui/component.mdx. On every one of them the driver kept this branch's side and silently dropped main's, so step 2 of the sanctioned sequence restored main's side into the worktree and this commit re-derives all four from the merged tree. What each side contributed, now both present: ui.txt / component.mdx main's element:text.variant widening to the published nine (#19019), plus this branch's evaluated-slot narrowing data.txt main's transport no-transactions declaration (#18890) and the $orderby dual declaration (#19018), plus this branch's `source: string` narrowing on ConditionalValidationSchema and CrossFieldValidationSchema automation.txt main's structured-region pause/end refusal (#18688), plus this branch's narrowing The two MIXED, deliberately unrouted paths were hand-resolved by git's ordinary three-way merge and verified rather than eyeballed: registry.ts's hand-written remainder (generated regions stripped with the merge script's own awk) is byte-identical across base, both sides and the merge, and its line count is exactly additive (17142 + 121 + 74 = 17337), with both sides' migration entries present by id. component.zod.ts is additive too (3750 + 4 + 45 = 3799) and its single .superRefine() is untouched. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
`packages/spec/src/data/driver.test.ts` pinned the `transactions` retired-key prescription with a regex requiring only that `METHOD PRESENCE` and `beginTransaction` appear somewhere in the thrown message. #18890 rewrote that prescription's opening from "gated on METHOD PRESENCE" to "gated on the DRIVER'S DECLARATION, no longer on METHOD PRESENCE alone" — and the old regex matches BOTH. Reverting the prescription to the now-false sentence kept the test green, so the pin could not fail on the one shape it exists to catch. The title said "points at method presence", which is the old world too. Anchor on the two things #18890 made load-bearing instead: the negation (`no longer on METHOD PRESENCE`) and the predicate that replaced presence (`driverSupportsTransactions`). `beginTransaction` and `Delete the key` stay, so nothing the old pin covered is dropped. Deliberately NOT a ban on the phrase `METHOD PRESENCE`: `schemaSync`'s own "Schema sync is gated on METHOD PRESENCE" sentence in driver.zod.ts is true of the runtime today and is untouched. Test-only. No runtime file, no published surface. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18063
Clause-②: yes
Governing text: maintainer decision batch #148 item 3, letter B, 「同意」 2026-09-17T14:26Z (issue comment 5716042163). It supersedes batch #133's route C. Quoted verbatim and untranslated, because the spelling delegation is the part this PR had to execute:
Notation. This body writes generic types bracket-free —
Promise[Knex.Transaction]means the declarationSqlDriverpublishes. That is a spelling choice against body sanitization, not a different type.What landed
packages/spec—DriverCapabilitiesgains one live bit,transactionsUnsupported, plus the predicate that reads it,driverSupportsTransactions(), exported from@objectstack/spec/data.packages/objectqlandpackages/core—typeof driver.beginTransaction:ObjectQL.transaction,ScopedContext.transaction,ScopedContext.txDriver()behind the discrete begin/commit/rollback trio, andengineCanRollBack— the ADR-0119 D4 gate that metadata-protocol's atomicbatchData/updateManyData/deleteManyDataandrunMigrationJournalshare. The degrade warning now says WHICH of the two reasons it fired for.⭐ The fourth gate was found by the at-tier review and is why this count changed. With only three re-keyed, the gates DISAGREED:⚠️ the pre-existing pin stayed GREEN under that ablation, which is why it never caught this.
driverSupportsTransactionssaid false whileengineCanRollBackstill read method presence and said true — and the D1 degrade swallowed the driver's refusal. Measured on the real chain: an atomicbatchDatareturned a 「rollback」 with one record persisted andbegins = 0, against a lit control (same double, bit removed) that threw 501 with 0 rows; the migration runner ran tocompletedand wrote thechunk_donemarker its own header says 「would not mean committed」. ⇒ this PR briefly re-opened the 「rollback does not roll back」 defect one layer up. Fixed, with a pin that fails without it — and3.
driver-sql—SqlDriver.supportsspellstransactionsUnsupported: false.SqlDriver.beginTransaction()keeps its narrowPromise[Knex.Transaction]; nothing in the base was widened.4.
driver-turso— the remote face declares the bit;TursoDriver.beginTransaction()publishes the inherited declaration instead ofPromise[any];RemoteTransportloses its three decorative transaction members.The spelling, priced — because the ruling's preferred spelling points at a tombstone
driver.zod.ts:266is the prescription line of a retired-key tombstone:transactionswas removed in@objectstack/spec17.0.0 under ADR-0049 enforce-or-remove, andsavepoints/isolationLevelsbeside it are the same retired family. Three spellings were priced before one was chosen.(a) Revive the name
transactions. Rejected, and the cost is measurable rather than aesthetic:packages/spec/src/migrations/registry.tsedited — the D3 entrydriver-capabilities-inert-bits-removednamesdata.DriverCapabilities.transactionsin itssurfacelist and states the count ("of 34 declared bits, THREE have a decision-making reader … THIRTY-ONE were written by every driver and read by nothing") in itsreasonandacceptanceCriteria. That file is MIXED and deliberately not routed to the os-regen merge driver, so it is the one file in this area that a merge cannot resolve mechanically. The chosen spelling touches it zero times (verified:git diff origin/main..HEADdoes not name it).falsewhen absent; a revivedtransactionsmust mean "yes, transactions" when absent, or every existing driver silently loses them on upgrade. That is a tri-state boolean in a record where nothing else is one.RETIRED_BITSindriver.test.ts, the prescription case, the 31-tombstone counts in two docblocks.(b) A new key outside
supports— dispreferred by the ruling by name, and larger: a second place to look for one fact.(c) A new inverse-polarity bit on the live
DriverCapabilitiesrecord. Chosen. It keepsabsence = false, leaves the tombstone true and refusing, touches neither MIXED file, and costs one key plus its reader.⭐ The ruling's stated preference and the tombstone do not conflict, and that is the finding worth stating plainly. "A capability bit is preferred over a new key" asks for a bit on the existing
DriverCapabilitiesrecord — it does not ask for the retired NAME back. Spelling (c) satisfies the preference in full while the tombstone stays exactly as published. No seat question is escalated here because there is no fork to escalate.Why adding a bit SATISFIES enforce-or-remove rather than reversing it
The 17.0.0 audit removed thirty-one bits for one stated reason: no code anywhere read them. It kept the three where method presence provably cannot carry the signal. Ruling B's entire content is the creation of the missing reader. The bit arrives with the engine dispatch that consumes it, in the same change — the honest order the ADR asks for — and the record's own docblock now states that bar for the next author.
Why method presence could not carry this
TursoDriver extends SqlDriver, whosebeginTransaction()opens a real knex transaction, so the inherited method reported the libSQL REMOTE transport as transactional. It is not:RemoteTransport's data methods take nooptionsargument at all, so a handle cannot reach the statement that would have to join it. A subclass cannot opt out of a door it did not open. This is the exact mirror ofbatchSchemaSync, which exists because a subclass can inheritsyncSchemasBatchfrom a base whose transport batches while its own cannot — and which the engine likewise ANDs with method presence.Premise check: part of this card was consumed while it sat in the box
Reported rather than quietly absorbed, because it changes what clause 3 still owed.
62bce5c297d— "refuse transactions on the remote face instead of silently dropping them (PR #18717)", 2026-09-17T17:13:06Z, four hours after the ruling — landed the driver-level loud refusal for card #18616:TursoDriverrefusesbeginTransaction()/commit()/rollback()and anyoptions.transactionon the remote arm. That card is alreadycompleted; this PR does not re-open or re-decide it, and deliberately does not add a second, engine-leveloptions.transactionrefusal beside the driver's — a second mechanism for zero additional drivers is the shape this whole card is about.What that leaves for this PR is the half nothing had built, and it is load-bearing:
⭐ The refusal's own remedy was unreachable.
refuseRemoteTransaction's message tells callers to "take the non-transactional path deliberately:engine.transaction()withoutrequire: trueon a datasource whose driver has no transactions runs the callback with no rollback and says so (ADR-0119 D1)". With the gate reading method presence, that path could never be taken for this driver — the method is there, so the engine opened a transaction and the callback got a 501 out ofbeginTransaction()instead of the declared degrade. The message made a promise only this change can keep.Two more premise readings, both against
origin/main:RemoteTransport's three transaction members were unreachable once the driver refused:remoteTransport.beginTransaction|commit|rollback— 0 call sites repo-wide, against a lit control of 7 lines calling other members of the same field. They are deleted here.TursoDriver.beginTransaction()'sanydissolves without paying either price [finding] Nine more IDataDriver doors are still masked asanyon the SQL drivers — the #17277 census answered triage's 「is aggregate the only one?」 with NO #17690 priced.refuseRemoteTransactionreturnsnever, so the remote branch is assignable to any declared return type and the only arm that still returns issuper.beginTransaction(). The override republishes the base's type. It is spelledReturnType[SqlDriver['beginTransaction']]and not the knex type directly, becauseknexis not a dependency ofdriver-turso(check:undeclared-dep-imports; the same constraint the doors suite'sKnexSliceworks around) — and deriving it from the base is the stronger pin.os-muskaccount is deactivated). Lit control: #18063 and #18116 read fine through the same instrument in the same round, so the 404s are a reading. Clause 4 of the ruling — "SqlDriver.beginTransactionkeeps its narrowPromise[Knex.Transaction](the honest narrowing #17690 protected)" — is therefore treated as the governing restatement. No claim is made here about #17690's original text.Behaviour change for a caller
On a datasource whose driver declares the bit,
engine.transaction()takes the DECLARED non-transactional path (ADR-0119 D1) instead of opening a transaction it cannot honour:require: the callback runs with no transaction,owned: false, and the degrade warns once per datasource — naming the declaration, not a missing method, because sending an operator to look for a method this class publishes wastes the report;require: true:TransactionUnsupportedErrorbefore the callback writes anything;ScopedContext.transactionanswers identically, and the discrete trio'sbeginreturnsnull.Every one of those is the answer a driver with no
beginTransactionalready received. Nothing that worked stops working — which is why the changesets are minor: the remote transport never honoured a transaction, so no working behaviour is withdrawn (the ruling's own stated ground, and the ground on whichRemoteTransport's three published members are removed at minor).Verification
All readings on the merged tree,
0c6eeea0f6f, againstorigin/main0b31d90fb37. Every exit code captured by redirect, never through a pipe.turbo run build, driver-turso + objectql closures)@objectstack/spec check:generatedspecsrc/data/driver.test.tsobjectql— 6 transaction suitesdriver-tursopnpm test(whole package)driver-sqlpnpm test(whole package)pnpm lint(eslint . --no-inline-config, whole repo)pnpm check:type-check-debtreturned exit 3,PREREQUISITE NOT MET— it refuses to measure without the whole workspace built, which is a farm-scale build. Recorded as NOT MEASURED, ⛔ not as a pass and ⛔ not as a red. The rest of the derived gate roster is CI's run.Reverse verification — two legs, both dist-aware
Leg A — the spec predicate (
objectqlresolves@objectstack/specthroughexports, i.e.dist/, per theKNOWN_UNALIASED_TEST_IMPORTSledger, so the mutation had to reachdist/to mean anything):driver.zod.tsblob at HEAD63ab873394848c025325ac234e8bd62b361c9ce0481e373c37ec774e03b6c127b6ceb6d8527c03d1— differs, so it reached diskablation-dist-preflight @objectstack/spec … --absentdist/engine-transaction-declared-unsupported.test.tsspecdriver.test.tsgit checkout HEAD -- …),git diff HEAD63ab873…Leg B — the driver declaration (same-package source resolution, no build in the path):
turso-driver.tsblob at HEADbb55757e6025d55d58babbbe0c090eefa4afa001transactionsUnsupported: false)2bfb64a95287d3145dea4d36fd75cad23868f3e1— reached disk; injected marker observed on diskgit diff HEADemptyPredicted direction was RED, and RED is what both legs produced. Both scripts carried
trap … EXIT INT TERMwith absolute paths; both restores are proven by blob identity and an emptygit diff HEAD, not by an exit code.*_SRC_COUNTecho lines were mis-quoted inside a quoted heredoc, sogrepread the pattern's tail as extra filenames and printed a prefixed0. Those two lines are void, not readings. The on-disk proof does not rest on them — it rests on the anchor assertion (the pre-mutation text had to occur exactly once or the script aborted), the injected marker counted on disk, and the two blob hashes.Merge
origin/mainwas merged after PR #18704 landed, throughscripts/pm/os-regen-merge.sh— merge committed first, regeneration afterwards, never during (agen:schemarun in MERGE state rolls the authorable-surface anchor back to the old fork point). Three os-regen artifacts were regenerated from the merged tree. Asserted afterwards: zero lines present inorigin/main'sapi-surface/data.json,authorable-surface/data.jsonorexport-origins/data.jsonare absent from the regenerated files, with the lit control firing (the single addition isdriverSupportsTransactions (function)). Neither MIXED file —dropped-refinements.baseline.json,packages/spec/src/migrations/registry.ts— is touched by this branch at all.Acceptance notes
Out-of-scope observations, noted and deliberately not acted on here:
packages/spec/src/data/driver.zod.tsstill carries the retiredsavepointsandisolationLevelsbesidetransactions; both remain correctly retired under this change and neither gained a reader. Noted, not filed.packages/objectql/src/engine.tshas a third comment (near thebatchDataobservability path) that citeswarnTransactionUnsupportedas its model; it is prose, still accurate, and left alone. Noted, not filed.turso-driver-doors-declared-types.test.tsheader still quotes a TS2416 coordinate (src/turso-driver.ts(1662,18)) that has drifted by landings. The receipt's substance reproduces; only the coordinate is stale, and it is kept verbatim as the historical error text. Noted, not filed.Generated by Claude Code
Generated by Claude Code