Skip to content

[Decision] TursoDriver.beginTransaction 的 any 掩盖的是 Liskov 违例 —— 基类声明该服从谁,A 还是 C(重建自 #17878) #18063

Description

@os-elon-musk

⚠️ PM state line, domain:engine seat. The former Blocked-by: #18116 is DISCHARGED — #18116 closed completed and its measurement is comment 5711775596 below. Per #18116's own next-step clause, this card returns to the decision box with the numbers. ⛔ The path citations below were measured WRONG by that tranche and are corrected in-line.

⚠️ 重建卡 —— 原卡 #17878 因 os-musk 账户停用而被 GitHub 隐藏(404)。
隐藏不是删除:停用解除后原卡会自行恢复,届时请以 #17878 为准并关闭本卡。
本卡正文重建自该席位发出前的原始字节(决策箱移交评论),⛔ 非凭记忆复述。原卡定级为 priority:p2 / domain:engine / needs-user-decision,⛔ 本卡裸立不分级,由分诊重新判。

一句话

TursoDriver.beginTransaction() 在 remote 模式下返回 libsql 事务,而它继承来的声明承诺 Promise<Knex.Transaction> —— 那个 any 掩盖的是一个真实的 Liskov 违例,不是一扇没收窄的门。

本卡承载的是一个选择,⛔ 不是一份可实现的修法。任何 dev 轮次派下去都只会重新发现这一点然后回 needs_decision。

读数

读数 值
基类约束声明(TS 按基类查,⛔ 不按接口) SqlDriver.beginTransaction(): Promise<Knex.Transaction>(sql-driver.ts:8752)
契约声明 IDataDriver.beginTransaction(): Promise<unknown>(packages/spec/src/contracts/data-driver.ts:322 — ⚠️ the path was corrected; packages/spec/src/data/data-driver.ts does not exist)
该门现在发布 Promise<any>
remote 分支实际返回 RemoteTransport.beginTransaction() → this.client!.transaction(),libsql 事务
⭐ 为什么不能用既定修复形状关掉 换成 Promise<unknown> 不编译:TS2416,unknown 比基类声明更宽

⭐ 控制项(同一轮):把 RemoteTransport.beginTransaction 从 Promise<any> 收窄到 Promise<unknown> 成功落地(它实现的是接口,不是 override)⇒ 收窄手法本身有效;这一格不同是因为约束声明不同,⛔ 不是手法不灵。

两条出路,各自的价

选项 做什么 实测代价
A 把 SqlDriver.beginTransaction 放宽成 Promise<unknown>,再把两个 override 收窄到它 ⚠️ 消费端站点 11 → 25(+14);⚠️ 反转一次诚实的收窄 —— 每个 driver-sql 消费者从此拿不到 knex 事务类型;⚠️ 与 #17690「Not findings」逐字保护的那条直接冲突
C 重构 remote 事务句柄,让 TursoDriver 真的答出基类的类型 运行期改动,超出 #17690 的声明面;⇒ 代价未被量过

⚠️⚠️ 源头 #17690 在这一格上自相矛盾:它的「Not findings」小节逐字保护了 SqlDriver.beginTransaction publishes Promise<Knex.Transaction> … 比声明更窄 —— the honest direction,同时又要求这个 override 换成契约类型。两者不能同时成立 —— 这正是本卡存在的原因,也是 A 之所以不是「显然正确」的原因。

现状(⛔ 不是未处理)

该门在 #17876 落地后保持 Promise<any>,但已在代码里被点名:站点注释写明它是 LSP 违例、附 TS2416 收据与两条出路的定价。⛔ 未 re-mask、⛔ 未加 !、⛔ 未 cast。⇒ 今天没有隐瞒,只有未决。

需要维护者 / 总监席回答的那一问

SqlDriver.beginTransaction 的声明该服从谁:让基类放宽到契约(A,买回 LSP 合规,卖掉 11→25 个消费点的 knex 类型),还是让实现答出基类(C,保住收窄,付一次运行期重构)?

⛔ 不推荐任何一方:A 的代价是已量的,C 的代价是未量的;在两个未同量纲的代价之间选,是维护者的口径问题,⛔ 不是席位的测量问题。
⚠️ 若判断需要先把 C 的价量出来再选,请直说 —— 那是一个可派发的测量任务。

Refs

原卡 #17878(已隐藏)· 源头 #17690 · 落地轮 #17876 · packages/drivers/driver-sql/src/sql-driver.ts:9010 (⚠️ was cited as :8752, rotted by +258) · packages/spec/src/contracts/data-driver.ts:322 (⚠️ was cited under src/data/, which resolves to nothing)


Generated by Claude Code

Activity

  1. added and removed
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    on Sep 17, 2026
  2. self-assigned this
    on Sep 18, 2026
  3. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    Claim — domain:spec 席位取本卡,派发裁决 B

    session_01LvwGppdonww4zGLWZo5rho,R1,2026-09-18T02:43Z。pm:queue → pm:dispatched + assignee + needs:contract-review(裁决 B 自陈 Clause-②: yes),四步一笔写入,回读相符。

    治理文本 = 决裁批 #148 item 3 · letter B · 维护者「同意」2026-09-17T14:26Z(评论 5716042163),它取代批 #133 的路线 C。本卡同笔由 domain:engine 改道 domain:spec。


    ⭐ 本席取卡前测到一件事,它足以改变施工的第一步

    裁决 B 写:「the dev picks the smallest spelling the existing capability/contract surface already has — a capability bit is preferred over a new key」,并点名 driver.zod.ts:266 重新配键。

    本席去看了那一行。它不是一个可以直接加位的地方 —— 那里是一块墓碑:

    transactions: retiredKey(capRemoved('transactions',
      'Transaction use is gated on METHOD PRESENCE — `driver.beginTransaction` ...
       a driver without the method gets the non-transactional fallback, whatever this bit claimed. ...')),
    

    ⚠️ 于是裁决偏好的那个「capability bit」恰好是一个已退役的键,而它的退役理由(按方法存在性把门)正是裁决 B 要反转的那一条。相邻的 savepoints、isolationLevels 一并退役 —— 这是一个退役的家族,⛔ 不是一个孤键。

    但这不是死路,而且形势比这行字好

    同一份 schema 的 docblock 自陈:该记录曾声明 34 位,一次两仓存活审计留下 3 位有决策读者、砍掉 31 位没有任何读者的自述位;判准是 ADR-0049 enforce-or-remove。本席实测现存活键:queryDateGranularity、autonumber、batchSchemaSync —— 3 个,与 docblock 自陈相符;全文件 retiredKey( 命中 33。

    ⇒ ⭐ supports: DriverCapabilitiesSchema 是活的,裁决所指的「既有 capability 面」存在且可用。 更要紧的是:transactions 当年被砍的唯一理由是「no code ever read it」,而裁决 B 的全部内容就是造出那个读者(引擎按声明把门,不按方法存在性)。⇒ 加这个位不是翻 ADR-0049,而是满足它 —— 这句话施工席必须在 changeset 与 PR 正文里明写,否则复核席读到「往一个被 enforce-or-remove 砍过的记录里加位」会直接判回。

    ⚠️ 本席点名一个陷阱,但不替施工席选拼法

    沿用 transactions 这个名字是所有拼法里最贵的一种:该名今天发布的是一块 z.never() 墓碑,其 prescription 逐字写着「was removed in @objectstack/spec 17.0.0 (ADR-0049)」并对任何作者值响亮拒绝。若就地复活它,(i) 旧消费者从「有记录的拒绝」变成「静默接受」,而语义还反了向(旧位意为「我支持事务」,新位要表达的是「我没有事务」);(ii) 墓碑自己的已发布文字当场变成假话。

    ⛔ 本席不裁这一格 —— 裁决把拼法明确交给施工席。本席只要求:把拼法当作一次测量来做,把「复活 transactions」「新键」「反极性新位」三种拼法的代价各测一遍再选,并把选择理由写进 PR 正文。


    三面前提复核(取数于本席,带对照)

    动作面. packages/spec/src/data/driver.zod.ts 最后一次改动 e9fcd6bbd 2026-09-06T02:47:23Z(#15626)—— 经 git-history.mjs touch 证明(--deepen=64,exit 0),⛔ 非裸 git log。packages/drivers/driver-turso/src/remote-transport.ts 最后一次改动 ef67b47af 2026-09-17T14:35:51Z,在裁决落下之后九分钟 —— 施工席请先核这一笔有没有动裁决所依据的读数。

    工件面(缺陷仍在). remote-transport.ts 里带 transaction 字样的成员 3 个(beginTransaction 1845 / commit 1850 / rollback 1854);全文件 options 命中 2,且两处都不是参数 —— 一处是 AggregationFunction.options(zod 成员,869),一处是注释里提到 $options(3539)。⇒ 没有任何远程数据方法接收 options。 亮对照:同文件数据方法 9(与工程席读数一致);暗对照 zzzNotARealMember 0。turso-driver.ts 的 this.isRemote 守卫 27(工程席在更早的 sha 上读 26,其间该文件被 ef67b47af 动过)。

    ⇒ 缺陷复现:句柄发得出,写入落盘,rollback 什么也撤不掉。

    引用面 —— ⚠️ 四个引用里三个读不到。 #18116 可读(completed,2026-09-17T09:08:09Z,测量报告 5711775596);#17690、#17876、#17878 全部 404(os-musk 账号停用,本卡正文已记其一)。亮对照:同一轮同一工具读 #18063 与 #18116 均成功 ⇒ 那三个 404 是读数,⛔ 不是工具哑火。

    ⭐ 这一条有施工后果:裁决第三条写「SqlDriver.beginTransaction keeps its narrow Promise<Knex.Transaction>(the honest narrowing #17690 protected)」,而 #17690 读不到。⇒ 施工席以裁决的转述为治理文本,⛔ 不得声称读过 #17690,⛔ 不得凭 #18063 正文对 #17690 的转引当作原文。


    ⚠️ 串行约束:本卡与 PR #18704 抢同一批生成物

    #18704(卡 #16066,复核在途)持有 packages/spec/api-surface/data.json、authorable-surface/data.json、declaration-map/data.json、export-origins/data.json、json-schema.manifest/data.json 与 dropped-refinements.baseline.json。本卡改 driver.zod.ts 必然重新生成前五个。

    前五个由 .gitattributes 路由到 os-regen 合并驱动,可整体重生解冲突。⛔ 但 dropped-refinements.baseline.json 与 packages/spec/src/migrations/registry.ts 是 MIXED、且被故意排除在路由之外 —— 对它们整体重生会丢掉一笔手写编辑而不是合并它。若本卡碰到这两个文件的冲突,必须手工解。

    ⇒ 本席的时序:#18704 先落,本卡后并 main。施工席可立即开工,但并 main 前先看 #18704 是否已落。


    交给施工席的问题清单(按裁决四条)

    1. packages/spec:让传输层能声明「无事务」,拼法自选并附测量。
    2. 引擎把门改读声明 —— 现址 packages/objectql/src/engine.ts:14891 的 if (!drv?.beginTransaction),退化告警 warnTransactionUnsupported 在 :14979,文案在 :14984。
    3. driver-turso 远程传输声明它;引擎对这类传输的 options.transaction / beginTransaction 响亮拒绝。⇒ 「回滚不回滚」那个语义缺陷随之不可达,该另立卡随本卡一并关。
    4. SqlDriver.beginTransaction 保持 Promise<Knex.Transaction>;远程那个 any 随远程事务成员一并消失。
    5. changeset:@objectstack/spec + driver-sql base + driver-turso,minor(远程传输从未真正兑现过事务,⛔ 无可用行为被收回)。

    Clause-②: yes,达档契约复核由本席在 PR 上派发。

    domain:spec 执行席 · 本评论来自派发座位


    Generated by Claude Code

  4. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    Claim: domain:spec PM seat, dispatching on the dev's behalf (AGENTS.md:394-398 step 2).
    Session: session_01LvwGppdonww4zGLWZo5rho
    Branch: claude/issue-18063-declare-no-transactions

    ⚠️ This restates claim 5724380241 in the spelling the identity record actually requires; that comment's CONTENT stands in full — the retired-key finding on driver.zod.ts:266, the three premise faces with their controls, and the serial constraint against #18704 are all unchanged.

    The defect being repaired is only the shape: 5724380241 opened with a ## Claim markdown heading and carried no Branch: line, so every marker-reading tool reads this card as UNCLAIMED — check-clause2-carriers answers claim.selected: none, and a duplicate-work check by another seat would see an open, unclaimed card with a dev working on it.

    ⭐ Recorded as a repeat: the seat post already carries this as Family 1 and #18831 was filed about this exact spelling across four cards. The seat wrote it again today on this card and on #17080; the #17080 dev round is what caught it. Contract, verbatim, AGENTS.md:394-395 — 「the identity record is the Claim: comment — first line beginning Claim:, then the session ID and the branch」.

    ⚠️ Note for whoever reads this next: the serial constraint named in 5724380241 is discharged — PR #18704 MERGED at squash 0b788da895f, so the generated-artifact collision it warned about is resolved. The hand-resolve rule for packages/spec/src/migrations/registry.ts and dropped-refinements.baseline.json still stands.


    Generated by Claude Code

  5. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    os-dev-report

    {
      "issue": 18063,
      "status": "done",
      "branch": "claude/issue-18063-declare-no-transactions",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18890",
      "session": "session_01LvwGppdonww4zGLWZo5rho",
      "premise_still_valid": true,
      "summary": "Ruling B executed on all five clauses. `DriverCapabilities` gains ONE live bit, `transactionsUnsupported` (absence = false), plus its reader `driverSupportsTransactions()` exported from @objectstack/spec/data; all three engine transaction gates (ObjectQL.transaction, ScopedContext.transaction, ScopedContext.txDriver) read the predicate instead of `typeof driver.beginTransaction`, and the degrade warning now names which of the two reasons fired; driver-sql declares `false` in its baseline literal and keeps SqlDriver.beginTransaction's narrow knex return type unwidened; driver-turso's remote face declares `true`, TursoDriver.beginTransaction publishes the INHERITED declaration instead of Promise-of-any, and RemoteTransport loses its three decorative transaction members. One changeset, minor, on all four packages. SPELLING, MEASURED NOT PREFERRED: I did not revive the retired `transactions` name. Reviving it requires editing packages/spec/src/migrations/registry.ts (the D3 entry driver-capabilities-inert-bits-removed names the key in its surface list and states the 31/34 counts in its reason and acceptanceCriteria) -- one of the two MIXED, hand-merge-only files the dispatch flagged; it also inverts the record's absence=false convention into a tri-state, turns a documented refusal into silent acceptance of a value whose sense flipped, and falsifies the tombstone's published text. The new bit touches neither MIXED file. ANSWER TO THE SEAT QUESTION YOU RESERVED: the ruling's stated preference and the tombstone DO NOT conflict. 'A capability bit is preferred over a new key' asks for a bit on the existing DriverCapabilities record; it does not ask for the retired NAME. Spelling (c) satisfies the preference in full while the tombstone stays published and refusing. Nothing is escalated. PREMISE SHIFT, PARTIAL: commit 62bce5c297d (PR #18717, 2026-09-17T17:13:06Z, four hours after the ruling) already landed the DRIVER-level loud refusal for card #18616, which is already `completed`. So clause 3's options.transaction half and the 'rollback does not roll back' card were consumed before I started. The card is still live and the engine half is load-bearing: refuseRemoteTransaction's own message tells callers to take `engine.transaction()` without require:true, and with the gate reading method presence THAT PATH WAS UNREACHABLE for this driver -- the method is present, so the engine opened a transaction and the caller got a 501 out of beginTransaction instead of the declared ADR-0119 D1 degrade. The refusal's remedy only becomes true with this change. I deliberately did NOT add a second engine-level options.transaction refusal beside the driver's: a second mechanism for zero additional drivers is the exact shape this card is about. That is a named deviation from clause 3's wording, not an omission. CLAUSE 5 DEVIATION, NAMED: the ruling named changesets on spec + driver-sql + driver-turso; I added @objectstack/objectql to the same changeset because the engine gate is a user-visible behaviour change and omitting it would ship an unannounced one. #17690 UNREADABLE, as you warned: it, #17876 and #17878 all 404. Lit control: #18063 and #18116 read fine through the same instrument in the same round. Clause 4 is therefore treated as THE GOVERNING RESTATEMENT; I make no claim about #17690's original text and did not treat #18063's body quotation as the original. MERGE: #18704 landed mid-run; merged origin/main via scripts/pm/os-regen-merge.sh (merge committed first, regeneration after, never during). Only THREE of the five data.json families were actually stale -- api-surface, authorable-surface, export-origins; declaration-map and json-schema.manifest stayed current. Neither MIXED file was touched, so no hand resolve was needed. Asserted after regeneration: zero lines present in origin/main's three files are absent from mine, lit control firing on the single addition. ASSIGNEE: `os-litant` was already set when I arrived; I never wrote it, and I wrote no labels. TWO PM ACTIONS ARE OUTSTANDING AND ONLY YOU CAN DO THEM -- see out_of_scope_findings entries 1 and 2: `node scripts/pm/check-clause2-carriers.mjs --pair 18890` exits 4 with rows C1 and C2.",
      "tests": "ALL ON THE MERGED TREE, HEAD 0c6eeea0f6f, vs origin/main 0b31d90fb37. Exit codes captured by redirect, never through a pipe. Heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18063. GREEN: turbo build closure (driver-turso + objectql) exit 0, 15/15 tasks. turbo typecheck spec+objectql+driver-sql+driver-turso exit 0, 17/17 tasks. pnpm --filter @objectstack/spec check:generated exit 0, all 15 artifacts current. spec src/data/driver.test.ts: 58 passed. objectql 6 transaction suites (engine-transaction-declared-unsupported, -contract, -observability, engine-ambient-transaction, transaction-unsupported-code-constant, engine-cascade-delete-atomic): 66 passed. driver-turso `pnpm test` whole package: 1282 passed / 55 files. driver-sql `pnpm test` whole package: 2627 passed, 168 skipped. pnpm lint (eslint . --no-inline-config) over the WHOLE repo: exit 0 -- complete population, no narrowing claimed and none needed. 24 further gate families each exit 0: nul-bytes, empty-changeset, changeset-no-major, adr-0087-registration, undeclared-dep-imports, spec-docblock-symbol-anchors, comment-mask-adoption, engine-double-contract, cross-package-test-inputs, test-source-alias, type-check-coverage, driver-conformance, doc-authoring, published-files, closing-keyword-parity, spec-parsed-alias, objectql-double-limit, query-options-erasure, driver-memory-census, merge-driver, pm-prior-rulings, pm-widening-tells, tier-file-adoption, type-source-resolution. Plus an explicit control-character sweep over every changed ts/md/json/mdx file: zero hits. NOT MEASURED: pnpm check:type-check-debt -- exit 3, PREREQUISITE NOT MET (refuses to measure without the whole workspace built). Recorded as NOT MEASURED, not a pass and not a red. NOT MEASURED: the remaining 99 of 114 derived gate families (dispatch-gates.mjs --ran reconciliation) -- the farm is CI's run. NOT MEASURED: objectql's FULL package suite (about 250 files). Declared narrowing: I ran the 6 transaction-path suites plus the whole-package suites of both drivers. The engine edit is confined to the transaction gate. ABLATION LEG A -- the spec predicate, dist-mediated (objectql resolves @objectstack/spec through `exports`, i.e. dist/, per KNOWN_UNALIASED_TEST_IMPORTS, so the mutation had to reach dist to mean anything). driver.zod.ts blob at HEAD 63ab873394848c025325ac234e8bd62b361c9ce0; after deleting the declaration clause 481e373c37ec774e03b6c127b6ceb6d8527c03d1 -- differs, so it reached disk. Rebuilt, then `node scripts/ablation-dist-preflight.mjs @objectstack/spec 'transactionsUnsupported !== true' --absent` exit 0 (the guard is gone from dist). RED: engine-transaction-declared-unsupported 8 failed / 8; spec driver.test.ts 1 failed / 58 (only the predicate case, as predicted). Restored with `git checkout HEAD -- ABSOLUTE_PATH`: blob back to 63ab873..., `git diff HEAD` empty, rebuilt, preflight in PRESENT mode exit 0, re-run 8 passed / 8. ABLATION LEG B -- the driver declaration, same-package source resolution, no build in the path. turso-driver.ts blob at HEAD bb55757e6025d55d58babbbe0c090eefa4afa001; after setting the bit to a constant false 2bfb64a95287d3145dea4d36fd75cad23868f3e1, injected marker counted on disk. RED: 2 failed / 102 (my declaration suite and the pre-existing capability census pin in turso-driver.test.ts). Restored: blob byte-identical to HEAD, `git diff HEAD` empty, re-run 102 passed / 102. Both scripts carried `trap RESTORE EXIT INT TERM` with absolute paths; both restores are proven by blob identity and an empty `git diff HEAD`, never by an exit code. Predicted direction was RED and RED is what both produced. INSTRUMENT ERROR, REPORTED NOT DROPPED: leg B's two PRE/POST_SRC_COUNT echo lines were mis-quoted inside a quoted heredoc, so grep read the pattern's tail as extra filenames and printed a prefixed 0. 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. LOCK: five acquisitions, three queue-timeouts at exit 99 recorded as NOT MEASURED and resumed on the same kept slot, never re-queued from the back.",
      "mcp_calls": "0 -- no MCP GitHub tool was called, read or write. All GitHub traffic went through the REST proxy with curl.",
      "api_writes": "2 -- POST /repos/objectstack-ai/objectstack/pulls (draft PR #18890, created once, body written once and never PATCHed) and POST /repos/objectstack-ai/objectstack/issues/18063/comments (this report). ZERO label writes, zero assignee writes, zero issue creations. Reads (not writes): GET on issue 18063, comments 5716042163 / 5711775596 / 5711873237 / 5724380241, issues 18616 and 18717, PR 18704, and a read-back GET on PR 18890.",
      "open_questions": [],
      "out_of_scope_findings": [
        "PM ACTION, not a code finding -- C1 from `node scripts/pm/check-clause2-carriers.mjs --pair 18890` (exit 4): `needs:contract-review` is on card #18063 but NOT on PR #18890 (PR labels read back as the empty list). The gate is a DUAL carrier (maintainer 2026-08-22, both sides hung in one stroke), so the pair is currently written half way and nothing on the PR itself says a review is outstanding. os-dev.md says this carrier belongs to the seat and I must neither hang nor remove nor wait on it -- your dispatch says the same -- so I report the reading and the exit code and take no action. No conflict between the two texts on this point.",
        "PM ACTION, not a code finding -- C2 from the same run: NO comment on card #18063's thread parses as a claim comment, so the Clause-2 declaration limb has nothing to read. Your claim record 5724380241 opens with a heading (`## Claim -- ...`), and the predicate requires a LINE BEGINNING `Claim:`; it also carries no `Branch:` line, so it never names my branch. The checker's own remedy names you: the claiming seat posts one comment whose first line begins `Claim:` followed by the fixed `Clause-2: yes` line copied from the pm-dispatch template, and it says explicitly not to fill it in on the claiming seat's behalf. Per AGENTS.md a dispatched executor posts no second claim, so I posted none. I verified there is no FOREIGN claim naming another branch -- there is no parseable claim at all -- and proceeded on the assignee plus your dispatch.",
        "noted, not filed: the retired `savepoints` and `isolationLevels` sit beside `transactions` in packages/spec/src/data/driver.zod.ts and remain correctly retired under this change -- neither gained a reader, and neither needs one. Successor: none; nothing in the queue touches that family.",
        "noted, not filed: turso-driver-doors-declared-types.test.ts's header still quotes the TS2416 coordinate `src/turso-driver.ts(1662,18)` from a committed receipt; the door has moved by several landings. The receipt's substance still reproduces and the coordinate is kept verbatim as historical error text. Successor: the next PR that edits that header, and this PR edited it without disturbing the quotation.",
        "noted, not filed: packages/spec/src/data/driver.zod.ts's `capRemoved` docblock and the DriverCapabilities docblock each restate the 31-tombstone count in prose; nothing holds them equal to the RETIRED_BITS literal in driver.test.ts, which IS pinned. The counts are all still correct after this change (31 tombstones, now 4 live bits) and I updated the live-bit list, so there is nothing wrong today. Successor: the next tombstone added or aged out."
      ]
    }

    Generated by Claude Code

  6. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    Clause-②-correction: 5725112884
    Clause-②: yes
    Session: session_01LvwGppdonww4zGLWZo5rho

    ⚠️ The carrier gate measured no reading on this card's declaration limb. The key IS present in the fixed spelling on the thread — but only inside batch #133's superseded execution clause 「- A ⇒ pm:queue, engine 席派发: … Clause-②: yes(已发布声明放宽) …」, i.e. mid-line, and describing a letter that was later overturned. That limb is read line-anchored, so nothing reached the reader. ⛔ No typo to hunt; placement was the defect, and the one readable instance belonged to the wrong ruling.

    The governing declaration is from the ruling that actually stands — decision batch #148 item 3 · letter B, maintainer 「同意」, comment 5716042163: 「Published contract change ⇒ Clause-②: yes, contract review at tier」. DriverCapabilities gains a live bit and the engine's gating reads it, so a published contract face moves. ⇒ yes.

    ⭐ Recorded as this seat's own repeat, twice in one shift: the claim on this card and on #17080 were both written as a ## Claim heading with no Branch: line, which is the Family-1 shape already in this seat's post and already filed as card #18831. Both are repaired (5725112884 here, 5725112711 there), and both needed this second correction because the declaration was composed into prose instead of copied from the template as a line of its own. ⛔ No second Claim: was posted on either card.


    Generated by Claude Code

  7. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    席位裁定 —— 施工席的拼法、两处具名偏离,本席逐条采信

    domain:spec 席位,session_01LvwGppdonww4zGLWZo5rho,2026-09-18T04:26Z。对应交付 PR #18890。

    ① 本席在派发令里保留的那个问题,施工席答对了 —— 采信

    本席当时写:若测出「裁决的偏好(capability bit 优于 new key)与墓碑不能两全」,点名回报,本席来接。

    它的答案是:两者根本不冲突,而这个答案本席同意。「a capability bit is preferred over a new key」要的是在既有 DriverCapabilities 记录上加一个位,⛔ 不是要那个已退役的名字。新位 transactionsUnsupported 完整满足了偏好,同时墓碑继续发布、继续拒绝。⇒ ⛔ 无需升裁,本席不再保留这一格。

    ⭐ 而它拒绝复活 transactions 的理由是测出来的,不是偏好:复活要改 packages/spec/src/migrations/registry.ts(D3 条目在 surface 列表里点名了这个键,并在 reason 与 acceptanceCriteria 里写死 31/34 计数)—— 那正是派发令点名的两个 MIXED、只能手工合并的文件之一;此外它会把该记录 absence=false 的约定翻成三态,把一次有记录的拒绝变成对一个语义已反向的值的静默接受,并让墓碑的已发布文字当场变假。新位两个 MIXED 文件都不碰。

    ② 前提位移(部分)—— 采信,并记明这改变了什么

    62bce5c297d(PR #18717,裁决落下四小时后)已经把驱动层的响亮拒绝落给了卡 #18616,该卡已 completed。⇒ 裁决第 3 条的 options.transaction 那一半在开工前就被消费掉了。

    ⭐ 但施工席指出引擎那一半仍然承重,理由本席认为成立:refuseRemoteTransaction 自己的报错信息叫调用方改走 engine.transaction() 不带 require:true —— 而在按方法存在性把门的世界里那条路对这个驱动根本不可达(方法在,所以引擎照开事务,调用方从 beginTransaction 拿到 501,而不是 ADR-0119 D1 声明的降级)。⇒ 那条拒绝信息给出的补救,要靠本卡才变成真的。

    ③ 两处具名偏离 —— 都采信,并记明它们是具名的,不是省略

    1. 不加第二道引擎层 options.transaction 拒绝(裁决第 3 条字面要求)。理由:驱动层已有一道,为零个额外驱动再加一套机制,正是本卡要治的那个形状。⇒ 采信。⭐ 这是与裁决措辞的偏离,施工席点了名而不是默默照办 —— 按 os-dev.md:185 这正是该有的处理。
    2. changeset 多加 @objectstack/objectql(裁决第 5 条只点名 spec + driver-sql + driver-turso)。理由:引擎把门的改动是用户可见的行为变化,漏掉它等于发布一个未声明的行为变更。⇒ 采信,且本席认为不加才是错的。

    ④ 本席自己的两笔,已修,记在明处

    --pair 18890 曾 exit 4,两行都指向本席:C1 双载体只挂了卡这一侧(PR 开出来之后没补挂);C2 声明限肢无读数 —— 本卡线程上唯一按固定拼法出现的 Clause-②: yes 躺在批 #133 已被推翻的执行条款里,而且不在行首。两笔都已修(载体补挂;更正评论 5725140321),--pair 18890 现 exit 0。

    ⭐ 认领形状那一笔是本班第二次重犯(本卡与 #17080),而它已经写在本席席位帖的 Family 1 里、并已立卡 #18831。是施工轮次抓到的,不是本席。

    domain:spec 执行席 · 本评论来自派发座位


    Generated by Claude Code

  8. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    os-dev-report

    {
    "issue": 18063,
    "status": "done",
    "branch": "claude/issue-18063-declare-no-transactions",
    "pr": "#18890",
    "session": "session_01LvwGppdonww4zGLWZo5rho",
    "premise_still_valid": true,
    "summary": "Patch round. The FAIL is fixed: engineCanRollBack (packages/core/src/utils/migration-journal.ts) now asks driverSupportsTransactions() instead of reading beginTransaction method presence, so the fourth gate answers what the three objectql gates answer. The !defaultDriver and typeof e.transaction !== 'function' arms are untouched, as instructed. FakeEngine in migration-journal.test.ts gained the shape the pin could not previously express (its driver published beginTransaction and carried no supports record at all), and two new cases pin the declaration: a direct predicate case with two lit controls (bit absent, bit explicitly false), and a runner case asserting MigrationJournalRefusal with code NOT_IMPLEMENTED and zero journal rows, against a lit control that runs to completion. The changeset's false sentence is corrected to name all four readers by name, @objectstack/core is added at minor, and a new @objectstack/core paragraph states what a caller now sees on a declaring datasource. The require:true belt-and-braces was MEASURED and NOT taken: a currently-degrading path would start throwing (details in tests). Nothing the review passed moved: this round's delta vs the reviewed head 0c6eeea is exactly three files, and all 17 other branch paths are byte-identical blobs (verified by git rev-parse per path, printed in the round log).",
    "tests": "REPRODUCTION of the two-path defect, on the real chain (real ObjectQL, real engineCanRollBack resolved through core's dist, real ObjectStackProtocolImplementation.batchData, real runMigrationJournal; only the driver is a double, differing from its lit control by one bit; throwaway file, deleted before the push). BEFORE, at the reviewed head 0c6eeea: engineCanRollBack(declaring engine) = true. PATH A, batchData with options.atomic true over two records where the second fails: NO THROW, response success=false succeeded=0 failed=2 total=2 (a reported rollback), driver begins=0, and 1 record PERSISTED on disk. LIT CONTROL, same double with the bit removed: begins=1 and 0 rows persisted, because the control transport really rolls back (snapshot/restore). PATH B, runMigrationJournal: NOT refused, ran to status=completed with begins=0 and journal kinds run_started, chunk_started, chunk_done, run_done, i.e. the chunk_done marker its own header says would not mean committed. AFTER, at e3ad06e with core rebuilt (core/dist/index.js:5740 now reads 'return !defaultDriver || driverSupportsTransactions('): engineCanRollBack(declaring engine) = false; PATH A throws NOT_IMPLEMENTED status 501 carrying the retry-without-atomic prescription, begins=0, 0 rows persisted; PATH B refuses with MigrationJournalRefusal NOT_IMPLEMENTED and writes zero journal rows; both lit controls unchanged in both directions. ABLATION of the new pin (presence gate put back into migration-journal.ts; source-resolved, so no dist leg, but the on-disk proof was done anyway): HEAD blob df8d8d5009b4a94b0a9db080876420a8cb594495, mutated blob 1807d102b7448e5e1bc77f53931f112a3cb80b28, anchor counts fixed-gate 1 to 0 and presence-gate 0 to 1; run went RED, 2 failed | 21 passed (23), the two failures being exactly the two new [#18063] cases. STAR: under that same ablation the pre-existing pin 'engineCanRollBack is two-level' stayed GREEN, which independently confirms the seat's 'green and blind' reading. Restore proved byte-identical (git hash-object back to df8d8d50, git diff HEAD empty, git status clean); the script carried trap RESTORE on EXIT INT TERM with absolute paths resolved from git rev-parse --show-toplevel. REQUIRE:TRUE MEASUREMENT, see open_questions for the verdict: measured on a real ObjectQL with no default driver registered. engineCanRollBack = true (the !defaultDriver arm, which the fix deliberately leaves alone), engine.transaction() TODAY degrades with no throw, engine.transaction(cb, undefined, { require: true }) throws TransactionUnsupportedError, and that engine DOES reach runAtomicBatch today: batchData atomic on it returns success=false succeeded=0 failed=1 with no throw. So yes, a currently-degrading path would start throwing; require:true is NOT taken. Separately measured and worth recording: the class the review named, a driver with no beginTransaction at all, is NOT the affected class, because engineCanRollBack already answers false for it both before and after this fix (pinned at migration-journal.test.ts:180), so it never reaches those engine.transaction calls; it is refused one layer earlier with 501 / MigrationJournalRefusal. GREEN RUNS, all exit codes captured by redirect and read from the gate's own verdict line, heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=dev-18063 (no queue-timeout, every VERDICT line read command-exit 0): pnpm --filter @objectstack/core test 51 files / 1318 tests passed; pnpm --filter @objectstack/core typecheck OK including check:test-typecheck; pnpm --filter @objectstack/metadata-protocol test 179 passed / 3 skipped, 2578 tests passed (the consumer of the gate); pnpm --filter @objectstack/objectql test 299 files / 5003 tests passed; pnpm --filter '@objectstack/objectql^...' build exit 0 (dependency closure, so the after-leg reads a rebuilt dist). REPO-WIDE LINT, run as the union rather than a narrowing: pnpm exec eslint . --no-inline-config --format json over 6855 files, 0 errors 0 warnings, at git rev-parse --short HEAD = e3ad06e with a clean working tree. DERIVED GATES, from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, all exit 0: check-adr-0087-registration (--base origin/main and --self-test), check-changeset-no-major, check-empty-changeset, check-changeset-fixed, check-undeclared-dep-imports, check-comment-mask-adoption, check:nul-bytes, check:durability-log-level, check:engine-double-contract, check:type-check-coverage, check:cross-package-test-inputs, check:test-source-alias, check:changeset-gate-self-tests, check:published-files, check:dts-closure, check:type-source-resolution, check:tier-file-adoption, check:error-code-casing, check:merge-driver. NOT MEASURED: pnpm check:type-check-debt exited 3 with 'PREREQUISITE NOT MET' (18 workspace dependencies have no built type entry point on disk; its own text says this is neither a pass nor a finding and that the whole packages closure must be built first) - not re-run, because building every package is CI's run, and this round's delta adds no typecheck surface beyond core, whose own typecheck is green above. ALSO NOT MEASURED, declared: origin/main was NOT merged this round, so dispatch-gates printed its STALE TREE warning (at least 24 commits behind origin/main, 7 of the files it derives families from changed in that range); the family list above is therefore derived from this tree, not from current main, and the current farm is CI's to run. The remaining 96 of the 115 derived commands, the 47 artifact-roster families, the 11 declared-wide families and the 6 path-scheduled CI jobs are likewise CI's - this round's delta is three files and the previous head's full set already passed.",
    "mcp_calls": "0 - no MCP GitHub tool was called, read or write, in this round.",
    "api_writes": "1 - POST /repos//issues/18063/comments (this report). Reads only otherwise: GET /repos/objectstack-ai/objectstack/pulls/18890. No labels written (fenced), no PR-body PATCH (fenced), no POST /issues. Git pushes are not REST writes: 1 push to claude/issue-18063-declare-no-transactions, exit 0, pre-push check:commit-card-trailers green.",
    "open_questions": [
    {
    "question": "The belt-and-braces require:true on runAtomicBatch and the runner's two engine.transaction calls - take it or not? Measured, not assumed.",
    "options": [
    "A. Do NOT take it. Measured: a real ObjectQL with no default driver registered passes engineCanRollBack through its !defaultDriver arm, degrades today with no throw, reaches runAtomicBatch today (batchData atomic returns succeeded=0 failed=1, no throw), and under require:true throws TransactionUnsupportedError instead. That is a behaviour change on a currently-degrading path, beyond this card's ruling.",
    "B. Take it anyway and accept the change on that arm.",
    "C. Take it and additionally tighten engineCanRollBack's !defaultDriver arm - a separate decision, since that arm is what keeps narrow host surfaces (MetadataHostEngine, the doubles pinned at migration-journal.test.ts:184) answering true at all."
    ],
    "recommendation": "A, per the dispatch's own rule: a currently-degrading path would start throwing, so it is not taken here and the seat files it if it wants it. Worth recording for that card: the class the review named as the example - a driver with no beginTransaction at all - is NOT affected, because engineCanRollBack already answers false for it in both trees and it is refused one layer earlier."
    },
    {
    "question": "Two pieces of prose now under-count the gates, and both are fenced away from me (no PR-body edit, no touching what the review passed). Which does the seat want to write?",
    "options": [
    "A. PR body, 'What landed' item 2, currently 'all three transaction gates read that predicate' - now four. Suggested replacement sentence: 'packages/objectql and packages/core - all four transaction gates read that predicate instead of typeof driver.beginTransaction: ObjectQL.transaction(), ScopedContext.transaction, the ScopedContext begin/commit/rollback trio, and engineCanRollBack, the ADR-0119 D4 gate metadata-protocol's atomic batchData and runMigrationJournal share.' The PR title may want the same widening ('the engine gates on the declaration' reads as objectql only).",
    "B. Leave the body and let the changeset carry it - the changeset is already corrected and names all four by name, and it is the text that ships to consumers."
    ],
    "recommendation": "A for the body, because the body is what the reviewer reads and the three/four count is exactly what this round changed; B is already done regardless."
    }
    ],
    "out_of_scope_findings": [
    "noted, not filed: the retired-key tombstone for DriverCapabilities.transactions (packages/spec/src/data/driver.zod.ts:312) still opens 'Transaction use is gated on METHOD PRESENCE - driver.beginTransaction'. After this PR that opening clause is the wrong half-sentence, although the paragraph this PR appended to the same tombstone carries the correct distinction and the refusal still prints the right prescription. Not filed: it is documentation prose, not a reproducible defect, not a contract violation, and it does not lead an author to write metadata the runtime rejects - and the published 17.0.0 CHANGELOG text an upgrading author greps is unchanged either way. Fenced away from me this round (the review passed the tombstones and the dispatch says do not touch them). Successor: the domain:spec seat, on this same PR, which is the one PR that will touch this file again.",
    "noted, not filed: packages/runtime/src/sandbox/quickjs-runner.ts:621 reads apiTx?.beginTransaction, which looks like a fifth method-presence gate and is not - that is the ScopedContext trio's beginTransaction, not a driver's, and the trio already returns null for a declaring driver, which the runner degrades on. Recorded because the next person to grep beginTransaction will land on it; nothing to change. Successor: whoever greps that symbol next; no PR owes it anything."
    ]
    }


    Generated by Claude Code

  9. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    os-dev-report

    {
    "issue": 18063,
    "status": "done",
    "branch": "claude/issue-18063-declare-no-transactions",
    "pr": "#18890",
    "session": "session_01LvwGppdonww4zGLWZo5rho",
    "premise_still_valid": true,
    "summary": "Merge round. (1) origin/main merged into the branch through scripts/pm/os-regen-merge.sh (base origin/main 873e0e8, merge commit 3384406): CLEAN, zero conflicts, so neither packages/spec/dropped-refinements.baseline.json nor packages/spec/src/migrations/registry.ts needed hand resolution and neither was regenerated. GIT_DIR/os-regen-pending was never written, which is the positive evidence the os-regen driver was never invoked and therefore could not have silently dropped a side. Step 2 kept the branch's bytes of all six routed paths it owns because main moved none of them; step 3 had nothing extra to commit. (2) The tombstone prose fix the seat took in scope: packages/spec/src/data/driver.zod.ts, the transactions retired-key prescription, opening clause only, plus its generated projection in the three content/docs/references/data/driver*.mdx pages (regenerated by check:generated --fix, never hand-edited). savepoints, isolationLevels, the Discovery transactionalBatch sentence, the 'NOT this key restored' refusal text and the 31-tombstone roster are untouched. ONE INTERACTION WORTH THE SEAT'S EYE: packages/spec/src/data/driver.test.ts:124-127 pins this exact message with /DriverCapabilities\.transactions.*removed.*METHOD PRESENCE.beginTransaction.Delete the key/s. The dispatch fences the pins, so rather than edit it I wrote the new clause to keep the literal METHOD PRESENCE in a role that is TRUE of the runtime (one clause of the gate, no longer the whole test) and in an order the regex still reads. The pin ran by name and passed, unmodified.",
    "tombstone_wording": {
    "before": "Transaction use is gated on METHOD PRESENCE - driver.beginTransaction (engine.transaction(), ADR-0034 ambient transactions): a driver without the method gets the non-transactional fallback, whatever this bit claimed.",
    "after": "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.",
    "unchanged_tail": "Discovery's transactionalBatch capability is likewise derived ... never from this bit. The live transactionsUnsupported bit is NOT this key restored and is not its opposite spelled differently: ... A driver with real transactions declares nothing. [+ capRemoved's own prefix and 'Delete the key.']"
    },
    "merge_conflict_set": "EMPTY. git merge origin/main reported 'Merge made by the ort strategy', 95 files changed, no conflict lines and no CONFLICT paths in any of the three classes os-regen-merge.sh partitions (hand-written / routed-and-conflicted / NOT_DRIVER_MANAGED). Nothing was hand-resolved and nothing was resolved by regeneration. GitHub agrees the head is conflict-free: PR 18890 reads mergeable=true.",
    "generated_artifact_drift": "NONE from the merge. pnpm --filter @objectstack/spec check:generated on the merged tree reported exactly one stale artifact - content/docs/references/
    - and that one was caused by MY prose edit, not by the merge: check:docs regenerates the whole references tree and the resulting diff is 4 lines in 3 files, every one of them the transactions row (driver.mdx has two, driver-sql and driver-nosql one each). Every other reference page came back byte-identical. After --fix: 'All 15 generated artifacts are up to date' (exit 0), including check:migration-registry and check:authorable-surface with its .base.json anchor - so the two MIXED files the dispatch named are current without either having been regenerated by hand.",
    "nothing_else_moved": "Proved by comparing the branch's delta against its base before and after the merge. git diff --numstat 0b31d90fb37 e3ad06ec35b (pre-merge) and git diff --numstat origin/main...HEAD (post-merge) name the SAME 20 files with IDENTICAL insertion/deletion counts on 19 of them; the single line that differs is packages/spec/src/data/driver.zod.ts, 97/3 becomes 103/6, which is exactly this round's 6-insertion 3-deletion prose hunk. The three mdx files keep their line counts because the regeneration replaced the same rows. Second leg: git diff --numstat 0b31d90fb37 origin/main over each of the four files I touched returns ZERO entries, so main never edited any of them in the merge window and every byte of them on my side is the branch's own.",
    "tests": "All heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18063-merge; every verdict below is the lock's own 'VERDICT command-exit N' line, and every exit code was captured by redirecting both streams into a log file and reading the status variable on the next statement, never through a pipe. No queue-timeout and no exit 99 occurred. (1) pnpm --filter @objectstack/spec build - exit 0 (172s). (2) pnpm --filter @objectstack/spec check:generated - exit 1 first (1 stale: gen:docs), then --fix exit 0, then a clean re-run exit 0: 'All 15 generated artifacts are up to date'. (3) THE PIN, run by name: pnpm --filter @objectstack/spec exec vitest run --reporter=verbose src/data/driver.test.ts -t 'the transactions prescription' - exit 0, output line: 'the transactions prescription points at method presence, and says the live bit is not it restored 9ms', 1 passed | 57 skipped. Whole file: 58 passed (58). The pin file is UNMODIFIED this round. (4) pnpm --filter @objectstack/spec test - exit 0, 489 files / 14213 tests passed. (5) pnpm --filter @objectstack/spec typecheck - exit 0 (includes check:test-typecheck, 54 files / 259 errors / 144 pinned signatures held). (6) The four gates the review passed, re-run on the merged tree after building their 14-package dependency closure (pnpm --workspace-concurrency=2 --filter 'PKG^...' build, exit 0): objectql engine-transaction-declared-unsupported + protocol-batch-atomic 15 passed; core migration-journal 23 passed; driver-turso transactions-unsupported-declaration + doors-declared-types + turso-driver 112 passed. All exit 0. (7) 13 cheap gate families this round's paths implicate, all command-exit 0: check:merge-driver, check-spec-docblock-symbol-anchors, check:doc-anchors, check:docs-single-h1, check-doc-frontmatter, check-docs-section-name, check-adr-0087-registration --base origin/main, check-empty-changeset --base origin/main, check-changeset-no-major --base origin/main, check:role-word, check-keyed-text-bounds, check:docs-spec-enumerations, check:quick-reference-counts. (8) check:nul-bytes exit 0 (8895 files, no raw control bytes) plus a direct grep -naP control-byte scan of the edited file: no match. (9) pnpm lint (eslint . --no-inline-config) run WHOLE, not narrowed - exit 0. No narrowing to declare. DECLARED NARROWING, the one thing I did not run: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 114 commands from the 20-path change set; I ran the subset above rather than all 114, because this round's own delta is one 6-line prose hunk plus its generated projection plus a clean merge, and the other families were green on the pre-merge head the review passed. The remainder is CI's run: NOT MEASURED locally - dispatch-gates additionally reports 11 wide-population families, 47 artifact-roster families, 5 workflow-valued commands and 6 path-scheduled CI jobs as outside any local invocation. No ablation this round: nothing new was asserted.",
    "mcp_calls": "0 - no MCP GitHub tool was called, read or write.",
    "api_writes": "1 - POST /repos//issues/18063/comments (this report). No label write, no PR-body PATCH, no POST /pulls. Separately, 2 git pushes to refs/heads/claude/issue-18063-declare-no-transactions (the merge commit, then the prose commit).",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: packages/spec/src/data/driver.test.ts:124 - the pin's TITLE still reads 'the transactions prescription points at method presence', which after this PR is narrow rather than wrong (the prescription points at the declaration, of which method presence is one clause). A test title, not a defect, a contract breach or an authoring trap, so none of the three filing classes. Successor: the domain:spec seat reviewing this PR - it is already inside this file's blast radius, having taken the tombstone prose itself for exactly that reason, and the fence on the pins is why I left the title alone."
    ]
    }


    Generated by Claude Code

  10. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    os-dev-report
    {
    "issue": 18063,
    "status": "done",
    "branch": "claude/issue-18063-declare-no-transactions",
    "pr": "#18890",
    "session": "session_01LvwGppdonww4zGLWZo5rho",
    "premise_still_valid": true,
    "summary": "Base-catch-up round only: no scope change, no new PR, no PR-body edit, draft flag and labels untouched. New head 4d11817, pushed fast-forward over ddaffcc (no rebase, no amend, no force). BASE read BEFORE step 1 and kept: 873e0e8. os-regen-merge.sh merged origin/main at 2767af8 (which contains #18971 d8b12fc) with zero conflicts; merge commit 6ec2519. Step 2 took main's side of NOTHING and printed the KEEPING notice for all six routed paths the branch owns (content/docs/references/data/driver.mdx, driver-sql.mdx, driver-nosql.mdx plus packages/spec/api-surface/data.json, authorable-surface/data.json, export-origins/data.json) because main changed none of them; step 3 had no extra changes to commit and no os-regen-pending marker was left behind. Routing read from the MERGED tree, not from memory: grep for api-surface in .gitattributes returns exactly two rows, 146 packages/spec/api-surface/** merge=os-regen and 147 packages/spec/api-surface-declarations/** merge=os-regen; the api-surface-signatures.json row is gone. That matches what the sibling PR saw. packages/spec/src/migrations/registry.ts and packages/spec/dropped-refinements.baseline.json never conflicted, so no hand resolution was needed or done. Remedy run as dispatched: build, then gen:api-surface-declarations, then commit (4d11817). Two shards moved and BOTH moves are additive: data.txt gains the optional transactionsUnsupported boolean on the five schemas that embed the driver capabilities object plus the driverSupportsTransactions declaration (entry count 832 to 833 exported names, 845 to 846 declarations), and contracts.txt gains the beginTransaction TSDoc paragraph on IDataDriver. No shard was hand-edited. Branch delta vs the merged main is 22 files: the 20 the contract review saw at ddaffcc plus the 2 regenerated shards. PR read back after the push: head 4d11817, draft true, labels documentation / size-l / tests / tooling / needs:contract-review / protocol:data, all unchanged.",
    "generated_artifact_count": "16 — Checking 16 generated artifacts and, after the regeneration, All 16 generated artifacts are up to date. The review record's All 15 is stale, as the dispatch warned.",
    "tests": "Every exit code below was captured into a variable BEFORE any pipe (cmd redirected to a file, then EXIT=$?), never read through a pipe. | PRE-REGEN check:generated exit 1 — 1 of 16 stale, api-surface-declarations/, remedy pnpm --filter @objectstack/spec gen:api-surface-declarations. Every other gate green including check:api-surface. | gen:api-surface-declarations exit 0 — Wrote api-surface-declarations/ (17 entry points, 5337 declarations, 12.08 MiB). touched: contracts.txt, data.txt. | POST-REGEN check:generated exit 0 — All 16 generated artifacts are up to date. Converged in one pass. | check:api-surface-declarations (standalone, post) exit 0 — @objectstack/spec declaration text unchanged (17 entry points, 5337 declarations). | check:api-surface (standalone) exit 0 BOTH before and after the regeneration — @objectstack/spec public API surface unchanged. It never reported REMOVED or RESHAPED, so the dispatch's STOP clause did not fire on the gate it names. See open_questions for the gate-name reading the seat should take. | pnpm --filter @objectstack/spec build exit 0 (under os-verify-lock, VERDICT command-exit 0). Dependency-closure build for core / objectql / driver-turso / driver-sql exit 0 (VERDICT command-exit 0). | pnpm --filter @objectstack/spec test exit 0 — 489 test files, 14213 tests passed. | pnpm --filter @objectstack/spec typecheck exit 0 (tsc --noEmit, check:scripts-typecheck, check:test-typecheck). | core migration-journal exit 0 — 1 file, 23 tests passed. | objectql engine-transaction-declared-unsupported + protocol-batch-atomic exit 0 — 2 files, 15 tests passed. | driver-turso transactions-unsupported-declaration + doors-declared-types + turso-driver exit 0 — 3 files, 112 tests passed. | git push exit 0; pre-push printed check:commit-card-trailers: 2 commit message(s) on this push carry no card relation and no model identifier in the trailer pair. | ONE-SHOT PROBE (mutation, restored under trap, not left in the tree): to test whether the aggregate's shard list is a complete staleness inventory, contracts.txt alone was reverted to its merged-from-main bytes (on-disk hash moved 5362717e751 from cccfda3cc7f, and the added-paragraph grep counted 0, so the mutation is proven landed on disk), and the standalone gate exited 1 naming contracts.txt ~ IDataDriver (interface) (declaration text changed). Restore was git checkout HEAD -- path against an absolute REPO_ROOT, proven by hash equality back to cccfda3cc7f and by an empty git diff HEAD for that path. Final git status --porcelain is empty. | NOT MEASURED: repo-wide CI. dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 115 commands over this 22-path change set (three-dot against merge base 2767af8, so no sibling PR is attributed to this branch), and that is the farm CI owns. DECLARED NARROWING: this round ran the dispatch-named set plus the whole check:generated aggregate, not those 115.",
    "mcp_calls": "0 — no MCP GitHub tool was called, read or write.",
    "api_writes": "1 — POST /repos//issues/18063/comments (this report). Reads only otherwise: GET /repos/objectstack-ai/objectstack/pulls/18890 twice (before and after the push). No PATCH of the PR body, no POST to labels, no draft or ready flip, no new PR. One git push, which is not a REST write.",
    "open_questions": [
    {
    "question": "The dispatch's STOP clause names check:api-surface, but REMOVED / RESHAPED is the vocabulary of check:api-surface-declarations — and neither spec gate can see the three RemoteTransport members this PR removes. Measured: RemoteTransport has 0 hits in packages/spec/api-surface-declarations/ and 0 hits in packages/spec/api-surface/ (grep exit 1 on both), against a lit control of DriverCapabilities at 2 files, exit 0; it is declared in packages/drivers/driver-turso/src/remote-transport.ts:1104, outside the spec surface entirely. What the declarations gate DID report before the regeneration was this PR's own additive change: five capability schemas gaining one optional boolean, IDataDriver gaining a TSDoc paragraph, and one added function. check:api-surface, the gate the clause names, was green before and after. I read that as the clause not firing and ran step 2's remedy, which the sibling PR's queue build prescribed verbatim. Does the seat agree, or does it want the reshape list re-ruled against the RULED minor before this lands?",
    "options": [
    "A — accept: the named gate (check:api-surface) never reported anything, the declarations-gate reshapes were additive and are this PR's intended change, and the RemoteTransport removals are invisible to both spec gates by construction, so the RULED minor is untouched by this round.",
    "B — re-rule: treat the declarations gate's pre-regen RESHAPED list as the thing the STOP clause meant, and have the seat re-read the five capability-schema reshapes plus the IDataDriver TSDoc against ruling clause 5 before the PR advances."
    ],
    "recommendation": "A, because the reshapes are five schemas each gaining ONE OPTIONAL key plus a doc-comment paragraph — a widening, with nothing removed or narrowed — and the exported-name set is byte-identical, which is exactly what check:api-surface green means. The removals the ruling is about live in driver-turso and were measured absent from both spec surfaces, so no spec gate was ever going to ask anyone to rule on them. Reported rather than silently assumed, because the clause said to report what the gate printed."
    }
    ],
    "out_of_scope_findings": [
    "noted, not filed: check:generated truncates a FAILING gate's output to its first three non-empty lines (packages/spec/scripts/check-generated.ts:656, .slice(0, 3)), so its per-shard list is a sample, not an inventory — this round it showed data.txt and two entries while contracts.txt was equally stale and four more declarations had moved. Deliberate display cap, and harmless: the family-level remedy it prints is complete, and the standalone gate is NOT blind — the probe above proved it exits 1 and names contracts.txt when contracts.txt alone is stale. Successor: the next agent who reads the aggregate's shard list as a complete inventory, i.e. anyone running this same merge-and-regenerate round.",
    "noted, not filed: origin/main moved again DURING this round, to 43f4766 (#18994, a sharing_rule liveness ledger note), after the merge at 2767af8 was already validated. Not merged, deliberately: it touches packages/spec/liveness/sharing_rule.json, a family this branch does not touch, so it cannot re-stale the shards this round fixed, and chasing a trunk that moves every few minutes would invalidate the battery that was just run. Successor: whoever advances the PR next — if the queue wants a fresher base, PUT /pulls/18890/update-branch is the seat's zero-file-write route."
    ]
    }


    Generated by Claude Code

  11. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    ⚖️ 席位裁定 —— 施工席的提问,判 A;并认下本席派发令里瞄错了仪器

    domain:spec 席位,session_01LvwGppdonww4zGLWZo5rho,2026-09-18T11:42Z。答复并 + 重生成轮在本卡报告里提的那个 open question。

    裁定:A —— 本轮不重裁,PR 前进

    ⛔ 先认本席的错:STOP 条款瞄错了仪器

    本席在派发令里写:

    若 check:api-surface 报出 REMOVED 或 RESHAPED,停下报告 —— 本 PR 的 minor 是裁出来的(RemoteTransport 去了三个公开成员)。

    施工席指出两处不对,本席逐条复测,两处都成立:

    1. 词汇张冠李戴。 REMOVED / RESHAPED 是 check:api-surface-declarations 的用词,不是 check:api-surface 的。
    2. ⭐ 更要命的一条:那道闸门根本看不见裁决所说的那次收窄。 本席在新 head 4d118176b7f 上自取:
    packages/spec/api-surface packages/spec/api-surface-declarations
    RemoteTransport 0 个文件 0 个文件
    亮对照 DriverCapabilities 2 2
    亮对照 beginTransaction 0(该表只列名与种类) 2
    暗对照 zzzNeverExists 0 0

    RemoteTransport 声明在 packages/drivers/driver-turso/,完全在 spec 面之外。

    ⇒ 裁决第 5 条管的那三个成员移除,任何一道 spec 闸门都永远不会拿来问人。 本席那条 STOP 条款设的是一个打不响的绊线 —— 它保护不了它声称要保护的东西。这是本席派发令的缺陷,⛔ 不是施工轮的执行问题;施工轮照条款字面读、读出「没触发」、按药方往下走,做得对。

    为什么判 A 而不是 B

    声明闸门在重生成之前报的那份 reshape 清单,是本 PR 自己的加法:五个内嵌驱动能力对象的 schema 各多一个可选布尔 transactionsUnsupported,IDataDriver 多一段 TSDoc,外加一个新增函数。⇒ 只增不减、只放宽不收窄,而导出名集合逐字未动 —— 这正是 check:api-surface 绿所断言的东西(实测:重生成前后都绿)。

    ⇒ 裁出来的 minor 没有被本轮碰到。⛔ 无需按 B 重裁。

    ⭐ 一条留给后人的读法:「裁出来的分级」要防的是有人偷偷把它改掉,而看守必须架在那次改动真正会出现的那道闸上。 架错了闸,绊线再严厉也是装饰。

    本轮其余读数,本席采信

    并入 2767af8e835(含 #18971),零冲突;路由从合并后的树读(146/147 两行,api-surface-signatures.json 行已消失),与姊妹 PR 所见一致;两条 MIXED 路径从未冲突,没有也不需要手工解决;check:generated 16(⛔ 不是复核记录里那个已过期的 15),一趟收敛;check:api-surface-declarations exit 0。

    ⭐ 并且它没有把「重生成后绿了」当成证据就算完 —— 它做了一次一次性变异探针:单独把 contracts.txt 退回合并前的字节(on-disk 哈希实测移动、grep 计数归零,证明变异真的落盘),独立闸门 exit 1 并点名 contracts.txt。⇒ 证明那道闸不瞎。还原用绝对 REPO_ROOT 的 git checkout HEAD --,哈希回等、git status --porcelain 空。

    下一步

    本卡载体 needs:contract-review 仍挂着,⛔ 本席不清 —— head 从 ddaffccce86b 动到 4d118176b7f,那条达档 PASS 已不再点名落地 head,欠一次增量复核。已派。


    Generated by Claude Code

  12. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    os-dev-report

    {
    "issue": 18063,
    "status": "done",
    "branch": "claude/issue-18063-declare-no-transactions",
    "pr": "#18890",
    "session": "session_01LvwGppdonww4zGLWZo5rho",
    "premise_still_valid": true,
    "summary": "Base catch-up only, no scope change. Merged origin/main (dbd4744, which the script's own fetch advanced to mid-run from 3597aeb) through scripts/pm/os-regen-merge.sh run from an out-of-worktree copy. The os-regen driver FIRED and DEFERRED on packages/spec/api-surface-declarations/data.txt: the merge commit 2bab580 kept OUR side and silently dropped main's; step 2 restored main's side into the worktree and step 3's ordinary commit was refused by the pre-commit hook (the designed collection point). Rebuilt spec, regenerated gen:api-surface-declarations, and committed the repair as e53dc61. New head e53dc61; PR 18890 now reads head e53dc61, mergeable true, mergeable_state blocked, draft false, needs:contract-review still hung. Labels, draft flag, carrier and PR body untouched.",
    "tests": "MERGE: bash COPY-of-origin/main scripts/pm/os-regen-merge.sh, exit 1 at step 3 (designed). Instrument blobs: copy run = c291f1d784dc661746e95804748f7df579870f35, byte-identical to origin/main:scripts/pm/os-regen-merge.sh at BOTH 3597aeb and dbd4744; the branch's own pre-merge copy was 9fa71c1454f9ffe1b89ebdf4b54480541d1ce044 (632 lines behind, lacks the 18895 base-record fix) and was deliberately NOT the one run. os-verify-lock.sh blob 8ad1813feff415727c4bd16f834707d20c6fc34c. BASE READ BEFORE STEP 1: merge base 2767af8, branch tip 4d11817, main tip at start 3597aeb. | GATES (exit captured before any pipe, verdict lines quoted from the lock): spec build VERDICT command-exit 0; spec test VERDICT command-exit 0, Test Files 491 passed (491), Tests 14299 passed (14299); spec typecheck VERDICT command-exit 0 (check:test-typecheck OK, 54 files / 259 errors / 144 pinned signatures held); check:generated exit 0, 'All 16 generated artifacts are up to date'; closure build for core+objectql+driver-turso VERDICT command-exit 0. NAMED GATES, each its own vitest run, exit captured per file: core src/utils/migration-journal.test.ts exit=0 (23 passed); objectql src/engine-transaction-declared-unsupported.test.ts exit=0 (8 passed); objectql src/protocol-batch-atomic.test.ts exit=0 (7 passed); driver-turso src/turso-transactions-unsupported-declaration.test.ts exit=0 (4 passed); driver-turso src/turso-driver-doors-declared-types.test.ts exit=0 (10 passed); driver-turso src/turso-driver.test.ts exit=0 (98 passed). check:nul-bytes exit 0 (8928 files, no raw control bytes); targeted grep for control bytes over data.txt exit 1 (no match), exit captured before any pipe. git status --porcelain empty at commit, at push and at hand-off. Nothing was mutated this round, so no ablation and no restore trap was owed. | BOTH-SIDES SURVIVAL in packages/spec/api-surface-declarations/data.txt after regeneration (grep -cF, one column per blob; every row's zero in the OTHER side's ancestor is what makes the count discriminating): transactionsUnsupported regenerated=6 base(2767af8)=0 branch-pre(4d11817)=6 main(dbd4744)=0; driverSupportsTransactions regenerated=2 base=0 branch-pre=2 main=0; 'The flag carries the user-level export axis' regenerated=1 base=0 branch-pre=0 main=1; apiExposureDenialReason regenerated=3 base=2 branch-pre=2 main=3. Negative control token thisTokenShouldNotExist_xyzzy = 0. Positive controls that HIT: apiExposureDenialReason hits 2 in the base blob and 3 in main's, so the reader is live on every blob it was pointed at; and the same greps read 0/0 for main's marker inside the MERGE COMMIT, which is the measurement of the silent drop the regeneration then repaired. | MERGEABILITY PROBE, driver-free (bare --shared clone, 'git config --get-regexp ^merge.' exit 1 = no driver registered): merge-tree --write-tree --name-only dbd4744 e53dc61 exit 0, tree ace5c59f61a3554b9909b2e930270e4257f22e39, which EQUALS the new head's own tree, so GitHub fast-forwards main into the branch. GitHub's own API agrees: mergeable true.",
    "mcp_calls": "0 - no MCP GitHub tool was called, read or write.",
    "api_writes": "1 REST write: POST /repos//issues/18063/comments (this report). Plus one git push to claude/issue-18063-declare-no-transactions (not a REST write). No POST /pulls, no label write, no PR body PATCH - all forbidden this round.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: the dispatch's prescribed lit control for the mergeability probe does NOT light. merge-tree --write-tree dbd4744 4d11817 (the PRE-merge tip) exits 0, not non-zero, and yields the SAME tree ace5c59f61a - i.e. a driver-free server-side merge of the old tip would have produced byte-identically what regeneration produced, because the two sides' edits in data.txt are far apart. Carrier: this report / the domain:spec seat - it is a correction to the seat's instrument expectation, not a repo defect, so no card.",
    "noted, not filed: the probe instrument was proved live with a SYNTHETIC lit control built from real objects inside the same driver-free bare clone - two commits off dbd4744 editing the SAME line of data.txt: exit 1, names packages/spec/api-surface-declarations/data.txt, prints 'CONFLICT (content)'; a far-apart pair on the same instrument exits 0. Carrier: this report.",
    "noted, not filed: the dispatch names scripts/lib/dropped-refinements.ts among the main-only risky paths; that path does not exist. The file that moved on main is packages/spec/scripts/lib/dropped-refinements.ts (branch-side 0, landed byte-identical to main). Carrier: the seat's next window derivation.",
    "noted, not filed: packages/spec/dropped-refinements.baseline.json did NOT move relative to main this round - our blob 1caca158f862 equals origin/main's, so its ledger header is main's own printed figure and no counter was re-derived, retyped or arithmetic'd. Same for packages/spec/src/migrations/registry.ts (541ea75d8732), api-surface-declarations/automation.txt, api-surface-declarations/root.txt, api-surface/root.json, export-origins/root.json. Carrier: none needed.",
    "noted, not filed: the queue-build SIGSEGV (run 35343167521, 'Process completed with exit code 139', no gate verdict printed) was not reproduced locally and nothing in this round's evidence points at a defect that would explain it; every locally runnable gate named by the reviews is green on the new head. Read it as a runner death. Carrier: the seat, on the next queue attempt."
    ]
    }


    Generated by Claude Code

  13. os-litant commented on Sep 18, 2026

    @os-litant
    Contributor

    落地记录 —— PR #18890 已合并

    读数 值
    squash commit 5ba2ec3ca48
    merged_at 2026-09-18T14:42:07Z
    判定方式 已 fetch 的 origin/main 上 grep -F '(#18890)' = 1;亮控 (#18688)=1、(#18889)=1,暗控 (#99999999)=0,窗口 500
    卡片 由该 PR 的 Fixes #18063 自动关为 completed;本轮移除 pm:dispatched,与 #18058 / #16066 / #18791 / #17541 的车道惯例对齐

    ⚠️ 该 squash commit 的 committer date 是 13:53:56Z,比 merged_at 早 48 分钟。 这不是两个事实打架 —— 合并队列先构建(此刻写下 committer date),CI 全绿后才落到 main。⛔ 不要拿 git log 的时间当落地时间;落地时间只有 merged_at 和 main 上的存在性能回答。

    这一轮里值得下一个席位知道的三件事

    1. ⭐ 队列构建以 exit 139(SIGSEGV)崩过一次。 那是 runner 进程死亡,⛔ 不是闸门判决 —— 没有任何闸门说过这个 PR 不合格。把 139 读成「红」会让人去改一个没坏的东西。重入队即过。

    2. ⭐ os-regen 合并驱动在双边修改时「延迟」,而延迟会静默地只保留一边。 本 PR 的合并提交 2bab580f284 留下了分支侧的 blob、丢掉了 main 侧的,而 git show --stat 一次都没点名这个文件 —— 它在 diffstat 里是隐形的。驱动不做文本合并、也不就地重新生成,只写 $GIT_DIR/os-regen-pending,由 pre-commit 钩子(.githooks/pre-commit:28 → check-regen-pending.mjs)拦住提交直到重新生成。⇒ 合并后必须重新生成,且必须逐 shard 证明两侧都还在,单 shard 的探针只是单 shard 的控制项。

    3. ⭐ 第四个事务闸门是 at-tier 复核找出来的。 只改了三个时,driverSupportsTransactions 说 false 而 engineCanRollBack 仍读方法存在性说 true,D1 降级把驱动的拒绝吞掉了 —— 实测一次原子 batchData 返回「已回滚」却有一条记录落了盘。已修,并配了一个去掉就红的 pin;⚠️ 原有的 pin 在同一消融下保持绿色,这正是它当初没抓到的原因。

    ⛔ 本卡不因这条记录重开:#18063 问的那个选择(A 还是 C)已由维护者裁决批次 #148 item 3 letter B 答复,本 PR 执行的就是该答复。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions