包 :@objectstack/driver-turso(现居 objectstack-ai/cloud,publishConfig: restricted)
目标路径 :packages/drivers/driver-turso(新 packages/drivers/ 目录;现有四个 driver 与它同批 迁入——见「推进顺序」)
发现于 :核对 #4484 (findStream enforce-or-remove)时,发现该 remove 的工作范围只覆盖本仓三个 driver,cloud 侧 4 处实现完全在盲区——见 #4484 的补充评论。
核实基线 :objectstack @ 0a936ea,cloud @ 0070d51(pin 在 ad5fe25)
前置项 :#4646 / PR #4648 (gate 的零发现)— ✅ 已合入
⏸️ 排期:等 maintainer 通知后再动
四条决策点已全部裁定(见文末),方案完整可实施,但实施时机由 maintainer 掌握 :当前有多个 agent 在并行改 packages/plugins/driver-*(#4484 正在改三个 driver 的 findStream),此时任何 对这些包的路径改动都会与进行中的开发撞车。
待那批开发收尾、maintainer 通知后,一次性统一迁移 ——不做分批。别的 agent 请勿抢跑这个 issue。
摘要
TursoDriver 以 extends SqlDriver 的方式跨仓库继承 本仓的类,住在闭源 cloud 仓;而本仓的 runtime 已经把 turso 当一等公民 ——环境 provisioning 的默认偏好顺序第一位就是它。结果是:开源侧的代码路径引用着一个自己仓里既测不到、也 grep 不到的 driver,而闭源侧只能在每次 pin bump 时追赶父类的重构。
证据 0:它本来就在本仓,而且本仓至今为它保留着扩展点
CHANGELOG.md:2052-2060 记录了它的到来:
@objectstack/driver-turso plugin — Migrated and standardized the Turso/libSQL driver from @objectql/driver-turso into packages/plugins/driver-turso/ . The driver extends SqlDriver …
同一个 release 还做了这件事(CHANGELOG.md:2066-2073):
@objectstack/driver-sql — Protected extensibility — Changed private to protected for all internal properties and methods(knex、config、applyFilters、formatInput/formatOutput、introspect* 等约 25 个成员)。Enables clean subclassing for driver variants (Turso, D1, etc.)
也就是说:本仓的 SqlDriver 至今维持着一整片 protected 扩展面,专为一个已经不在本仓的子类而存在 ——而这份扩展契约在本仓没有任何子类去行使它,等于一个无人验证的 API surface。这不是「要不要迁进来」的问题,是「它被迁出去了,接口债留在了这里」。
证据 1:开源侧已把 turso 当默认
位置
内容
packages/runtime/src/http-dispatcher.ts:1324
provisioning 偏好顺序:turso → memory → 任意其他已注册 driver
packages/runtime/src/http-dispatcher.ts:1299
POST /cloud/environments 的 driver 参数示例即 memory | turso
packages/runtime/src/default-datasource-plugin.ts:70
提到 distribution 的 turso
packages/objectql/src/engine.ts:3133
turso 专属 的瞬时 fetch failed 重试
packages/objectql/src/plugin.ts:54, 88, 144
三处以 Neon/Turso 这类 latency-bound 远程 driver 为前提的设计推理(含 cold start 不被 sync 打死)
本仓有 turso 专属的容错逻辑和调度偏好,却没有 turso 的代码和测试。
证据 2:闭源侧在持续追赶父类
cloud 仓 packages/driver-turso 最近 5 个提交里 3 个是在追本仓 SqlDriver 的内部变化 :
0352d3c fix(driver-turso): remote 模式的 filter 列也读进驱动的存储形态 (#937) (#1003)
eca7f7e fix(driver-turso): remote 模式的写入与 comparand 接回 SqlDriver 的 temporal seam (#937) (#942)
6d1ab28 test(driver-turso): 接入 temporal conformance 矩阵——canonical + 遗留存储四条扫描 (framework#4191) (#938)
耦合面不是「用了个接口」而是继承 (turso-driver.ts:166),且 find / findOne / findStream / create / update 全部带 override。父类每次重构,子类都可能静默坏掉,只能在 bump SHA 时才发现 。
节奏差放大了这一点:本仓最近 50 个提交跨约 11 小时;cloud 的 50 个提交跨约 2.7 天。
证据 3:仓库级 gate 与清扫物理上看不见它
scripts/check-driver-conformance.mjs 的 scope 注释写得很清楚:
packages/plugins/driver-* — discovered from disk, never listed here, so a new driver package is in scope the moment it exists
这正是迁回的直接收益:turso 落盘即入矩阵 ,filter 组合语义(#3774 )、temporal 存储形态(ADR-0053)、确定性分页读(#4363 )三套 case-set 自动 held 住它,不再靠 cloud 侧手工「接入 conformance 矩阵」(6d1ab28 就是这么补的)。
同理,#4484 那种「全仓 grep + 删实现 + 删桩」的 ADR-0049 清扫,今天必然漏掉 cloud 的 4 处实现。这类清扫是本仓常规动作,每一次都有同样的漏网风险 。
拆分方案
cloud/packages/driver-turso 共 4,824 行。
迁到 packages/drivers/driver-turso (~4,162 行)
turso-driver.ts(896)— TursoDriver extends SqlDriver,迁后变成同仓继承,父类重构立刻可见,protected 扩展面终于有子类验证
remote-transport.ts(961)— 纯 @libsql/client 走 HTTP/WebSocket,无任何平台机密;文件头本来就是 Apache-2.0
index.ts(95)、libsql-sqlite-stub.testkit.ts(87)
测试:turso-driver.test.ts(1,135)、spec/turso*.{test,zod}.ts、turso-{,remote-}temporal-conformance.test.ts(591)、date-bucket-parity.test.ts(135)、remote-*.test.ts(262)
留在 cloud (662 行 + service-cloud 一处)
multi-tenant.ts(289)+ multi-tenant.test.ts(242)— 按租户路由、{tenant} URL 模板、group token、TTL 缓存
vector-poc.test.ts(131)— 已核实完全独立 :直连 @libsql/client 的 createClient,不 import TursoDriver;驱动四个源文件里 vector / embedding / F32_BLOB 零命中。留下来不需要从驱动里切任何东西。
service-cloud/src/control-plane-proxy-driver.ts — 控制面 org 注入
cloud 随后按既有的 link: + .objectstack-sha pin 消费开源版 driver;multi-tenant.ts 只 import 公开的 TursoDriver。
迁完后 cloud 侧 driver-turso 包的收尾 (建议,非阻塞):它将只剩一个多租户路由器和一个 vector PoC,已经不是一个 driver。两件小事值得顺手做:把包名/目录改成名副其实的(如 tenant-router),以及把 vector-poc.test.ts 挪进 packages/knowledge-turso——该文件 docstring 写明它的目的就是 "confirm before building knowledge-turso",那才是它的归属。
目录重组:packages/plugins/driver-* → packages/drivers/*
五个 driver(现有 driver-memory、driver-mongodb、driver-sql、driver-sqlite-wasm + 从 cloud 迁入的 driver-turso)同批 落入 packages/drivers/,让 driver 与 plugin 在目录上分开。
收纳范围(已裁定) :packages/drivers/ 只收 IDataDriver 实现 。knowledge-*(本仓 2 个 + cloud 的 knowledge-turso)、embedder-* 留在 packages/plugins/。这与 check-driver-conformance.mjs 的 scope 定义一致——该脚本已明确把「非 driver 的 case-set 消费者」排除在外,目录边界与 gate 边界因此重合,不产生第二套判断标准。
改名的影响面(已实测)
本仓
pnpm-workspace.yaml — 增加 packages/drivers/* glob
scripts/check-driver-conformance.mjs:70 — DRIVERS_DIR(test(drivers): a conformance run that discovers zero drivers is a failure, not an OK (#4646) #4648 已把 self-test 里的 driver-sql / >= 3 硬编码消除,现在只剩这一处常量要改)
.github/workflows/ci.yml:866 — 遍历 packages/triggers/* packages/services/* packages/plugins/* 的循环
.github/workflows/lint.yml:424 — 构建嵌套包的说明与逻辑
各 driver package.json 的 repository.directory
packages/spec/liveness/*.json 的 evidence 路径 (field.json、object.json、query.json 共 11 处指向 packages/plugins/driver-sql/...)——ADR-0087 registries,路径失效即证据链断裂
文档:README.md:287-289、ARCHITECTURE.md:250、content/docs/plugins/packages.mdx(4 处)、content/docs/protocol/objectql/{index,query-syntax}.mdx、examples/app-crm/README.md
pnpm-lock.yaml 重生
cloud 仓(跨仓,会断)
3 个 package.json 共 6 条 link:../../../objectstack/packages/plugins/driver-* 指向物理路径,目录一动全部失效:
packages/objectos-runtime/package.json:32,33,53(memory / sql / mongodb)
packages/driver-turso/package.json:34(sql)
packages/service-cloud/package.json:27,28,29(memory / mongodb / sql)
外加 lockfile 里十余条同路径 specifier。必须与一次 bump-objectstack 同批推进 ,否则 pin 前移时 cloud 直接装不上。
这里有个值得记下的递归:目录重组本身正好踩中它要解决的那个跨仓盲区 。本仓改目录的 PR 在自己仓 CI 全绿(cloud 的 link: 路径不在本仓任何检查的视野里),破坏只在 cloud 下次 bump 时出现——和 #4484 一模一样的形状。
推进顺序
✅ 修 gate 的零发现 — check-driver-conformance 的 audit 路径对「发现零个 driver」报 OK — 零发现该是失败,且唯一的守卫硬编码了 driver-sql #4646 / PR test(drivers): a conformance run that discovers zero drivers is a failure, not an OK (#4646) #4648 ,已合入 main。
⏸️ 等窗口 — 待进行中的 agent 批次收尾(含 IDataDriver.findStream 没有任何调用方,两个 driver 的实现还正好做了它承诺要避免的事(ADR-0049 enforce-or-remove) #4484 对三个 driver 的 findStream 改动),由 maintainer 通知开工。
一次性统一迁移 (一个 objectstack PR + 一个 cloud PR,同批 bump):
建 packages/drivers/,五个 driver 一起进(四个从 packages/plugins/ 平移,driver-turso 从 cloud 迁入)
同一 PR 内改 pnpm-workspace.yaml glob、DRIVERS_DIR、两个 CI workflow、各包 repository.directory
cloud 侧 6 条 link: 路径 + lockfile,配 scripts/bump-objectstack.sh
收敛文档与 packages/spec/liveness/*.json 的 evidence 路径(11 处)
cloud 侧收尾:driver-turso 残包更名 + vector-poc.test.ts 归入 knowledge-turso
为什么不分批 :早前版本计划「先只迁 turso,再分批搬其余四个」,理由是降低单次跨仓同步的冲突面。maintainer 裁定改为一次性,理由更强——在多 agent 并行窗口里,任何 对 packages/plugins/driver-* 的路径改动都会与进行中的开发撞车,分批等于把这个撞车面重复 N 次;而且分批必然产生「packages/plugins/ 与 packages/drivers/ 各有 driver」的双根中间态,DRIVERS_DIR 得先改成多根扫描、迁完再改回单根,凭空多一次 gate 改动和一段需要维护的过渡状态。等窗口静默后一次做完,冲突面和改动次数都最小。
决策(已裁定)
✅ 许可/商业 — 核心 driver 迁回本仓,作为公开 Apache-2.0 包发布。
✅ 多租户边界 — multi-tenant.ts 的按租户 provisioning 属云产品差异化能力,留 cloud。
✅ vector-poc — 先留 cloud(已核实与驱动零耦合,留下不需要切分驱动代码;建议后续归入 knowledge-turso)。
✅ packages/drivers/ 收纳范围 — 只收 IDataDriver 实现;knowledge-* / embedder-* 留在 packages/plugins/。
✅ 实施时机与批次 — 等进行中的 agent 批次收尾、maintainer 通知后,一次性统一迁移;不分批,不抢跑。
不做的代价
维持现状可行,但要接受:每次本仓 driver 契约或 SqlDriver protected 成员变更,都得有人记得 去 cloud 补一刀,而这份记忆目前不在任何检查里——#4484 已经演示了它是怎么丢的。最低限度的止血是在 packages/plugins/driver-sql(或迁移后的 packages/drivers/driver-sql)加一条提示:改动 IDataDriver 契约或 SqlDriver 受保护成员时,同步检查 cloud/packages/driver-turso。
包:
@objectstack/driver-turso(现居objectstack-ai/cloud,publishConfig: restricted)目标路径:
packages/drivers/driver-turso(新packages/drivers/目录;现有四个 driver 与它同批迁入——见「推进顺序」)发现于:核对 #4484(
findStreamenforce-or-remove)时,发现该 remove 的工作范围只覆盖本仓三个 driver,cloud 侧 4 处实现完全在盲区——见 #4484 的补充评论。核实基线:
objectstack@0a936ea,cloud@0070d51(pin 在ad5fe25)前置项:#4646 / PR #4648(gate 的零发现)— ✅ 已合入
摘要
TursoDriver以extends SqlDriver的方式跨仓库继承本仓的类,住在闭源 cloud 仓;而本仓的 runtime 已经把 turso 当一等公民——环境 provisioning 的默认偏好顺序第一位就是它。结果是:开源侧的代码路径引用着一个自己仓里既测不到、也 grep 不到的 driver,而闭源侧只能在每次 pin bump 时追赶父类的重构。证据 0:它本来就在本仓,而且本仓至今为它保留着扩展点
CHANGELOG.md:2052-2060记录了它的到来:同一个 release 还做了这件事(
CHANGELOG.md:2066-2073):也就是说:本仓的
SqlDriver至今维持着一整片protected扩展面,专为一个已经不在本仓的子类而存在——而这份扩展契约在本仓没有任何子类去行使它,等于一个无人验证的 API surface。这不是「要不要迁进来」的问题,是「它被迁出去了,接口债留在了这里」。证据 1:开源侧已把 turso 当默认
packages/runtime/src/http-dispatcher.ts:1324turso→memory→ 任意其他已注册 driverpackages/runtime/src/http-dispatcher.ts:1299POST /cloud/environments的driver参数示例即memory | tursopackages/runtime/src/default-datasource-plugin.ts:70tursopackages/objectql/src/engine.ts:3133fetch failed重试packages/objectql/src/plugin.ts:54, 88, 144本仓有 turso 专属的容错逻辑和调度偏好,却没有 turso 的代码和测试。
证据 2:闭源侧在持续追赶父类
cloud仓packages/driver-turso最近 5 个提交里 3 个是在追本仓SqlDriver的内部变化:耦合面不是「用了个接口」而是继承(
turso-driver.ts:166),且find/findOne/findStream/create/update全部带override。父类每次重构,子类都可能静默坏掉,只能在 bump SHA 时才发现。节奏差放大了这一点:本仓最近 50 个提交跨约 11 小时;cloud 的 50 个提交跨约 2.7 天。
证据 3:仓库级 gate 与清扫物理上看不见它
scripts/check-driver-conformance.mjs的 scope 注释写得很清楚:这正是迁回的直接收益:turso 落盘即入矩阵,filter 组合语义(#3774)、temporal 存储形态(ADR-0053)、确定性分页读(#4363)三套 case-set 自动 held 住它,不再靠 cloud 侧手工「接入 conformance 矩阵」(
6d1ab28就是这么补的)。同理,#4484 那种「全仓 grep + 删实现 + 删桩」的 ADR-0049 清扫,今天必然漏掉 cloud 的 4 处实现。这类清扫是本仓常规动作,每一次都有同样的漏网风险。
拆分方案
cloud/packages/driver-turso共 4,824 行。迁到
packages/drivers/driver-turso(~4,162 行)turso-driver.ts(896)—TursoDriver extends SqlDriver,迁后变成同仓继承,父类重构立刻可见,protected扩展面终于有子类验证remote-transport.ts(961)— 纯@libsql/client走 HTTP/WebSocket,无任何平台机密;文件头本来就是 Apache-2.0index.ts(95)、libsql-sqlite-stub.testkit.ts(87)turso-driver.test.ts(1,135)、spec/turso*.{test,zod}.ts、turso-{,remote-}temporal-conformance.test.ts(591)、date-bucket-parity.test.ts(135)、remote-*.test.ts(262)留在 cloud(662 行 + service-cloud 一处)
multi-tenant.ts(289)+multi-tenant.test.ts(242)— 按租户路由、{tenant}URL 模板、group token、TTL 缓存vector-poc.test.ts(131)— 已核实完全独立:直连@libsql/client的createClient,不 importTursoDriver;驱动四个源文件里vector/embedding/F32_BLOB零命中。留下来不需要从驱动里切任何东西。service-cloud/src/control-plane-proxy-driver.ts— 控制面 org 注入cloud 随后按既有的
link:+.objectstack-shapin 消费开源版 driver;multi-tenant.ts只 import 公开的TursoDriver。迁完后 cloud 侧
driver-turso包的收尾(建议,非阻塞):它将只剩一个多租户路由器和一个 vector PoC,已经不是一个 driver。两件小事值得顺手做:把包名/目录改成名副其实的(如tenant-router),以及把vector-poc.test.ts挪进packages/knowledge-turso——该文件 docstring 写明它的目的就是 "confirm before buildingknowledge-turso",那才是它的归属。目录重组:
packages/plugins/driver-*→packages/drivers/*五个 driver(现有
driver-memory、driver-mongodb、driver-sql、driver-sqlite-wasm+ 从 cloud 迁入的driver-turso)同批落入packages/drivers/,让 driver 与 plugin 在目录上分开。收纳范围(已裁定):
packages/drivers/只收IDataDriver实现。knowledge-*(本仓 2 个 + cloud 的knowledge-turso)、embedder-*留在packages/plugins/。这与check-driver-conformance.mjs的 scope 定义一致——该脚本已明确把「非 driver 的 case-set 消费者」排除在外,目录边界与 gate 边界因此重合,不产生第二套判断标准。改名的影响面(已实测)
本仓
pnpm-workspace.yaml— 增加packages/drivers/*globscripts/check-driver-conformance.mjs:70—DRIVERS_DIR(test(drivers): a conformance run that discovers zero drivers is a failure, not an OK (#4646) #4648 已把 self-test 里的driver-sql/>= 3硬编码消除,现在只剩这一处常量要改).github/workflows/ci.yml:866— 遍历packages/triggers/* packages/services/* packages/plugins/*的循环.github/workflows/lint.yml:424— 构建嵌套包的说明与逻辑package.json的repository.directorypackages/spec/liveness/*.json的 evidence 路径(field.json、object.json、query.json共 11 处指向packages/plugins/driver-sql/...)——ADR-0087 registries,路径失效即证据链断裂README.md:287-289、ARCHITECTURE.md:250、content/docs/plugins/packages.mdx(4 处)、content/docs/protocol/objectql/{index,query-syntax}.mdx、examples/app-crm/README.mdpnpm-lock.yaml重生cloud 仓(跨仓,会断)
3 个
package.json共 6 条link:../../../objectstack/packages/plugins/driver-*指向物理路径,目录一动全部失效:packages/objectos-runtime/package.json:32,33,53(memory / sql / mongodb)packages/driver-turso/package.json:34(sql)packages/service-cloud/package.json:27,28,29(memory / mongodb / sql)外加 lockfile 里十余条同路径 specifier。必须与一次
bump-objectstack同批推进,否则 pin 前移时 cloud 直接装不上。推进顺序
main。findStream改动),由 maintainer 通知开工。packages/drivers/,五个 driver 一起进(四个从packages/plugins/平移,driver-turso从 cloud 迁入)pnpm-workspace.yamlglob、DRIVERS_DIR、两个 CI workflow、各包repository.directorylink:路径 + lockfile,配scripts/bump-objectstack.shpackages/spec/liveness/*.json的 evidence 路径(11 处)driver-turso残包更名 +vector-poc.test.ts归入knowledge-turso为什么不分批:早前版本计划「先只迁 turso,再分批搬其余四个」,理由是降低单次跨仓同步的冲突面。maintainer 裁定改为一次性,理由更强——在多 agent 并行窗口里,任何对
packages/plugins/driver-*的路径改动都会与进行中的开发撞车,分批等于把这个撞车面重复 N 次;而且分批必然产生「packages/plugins/与packages/drivers/各有 driver」的双根中间态,DRIVERS_DIR得先改成多根扫描、迁完再改回单根,凭空多一次 gate 改动和一段需要维护的过渡状态。等窗口静默后一次做完,冲突面和改动次数都最小。决策(已裁定)
multi-tenant.ts的按租户 provisioning 属云产品差异化能力,留 cloud。vector-poc— 先留 cloud(已核实与驱动零耦合,留下不需要切分驱动代码;建议后续归入knowledge-turso)。packages/drivers/收纳范围 — 只收IDataDriver实现;knowledge-*/embedder-*留在packages/plugins/。不做的代价
维持现状可行,但要接受:每次本仓 driver 契约或
SqlDriverprotected成员变更,都得有人记得去 cloud 补一刀,而这份记忆目前不在任何检查里——#4484 已经演示了它是怎么丢的。最低限度的止血是在packages/plugins/driver-sql(或迁移后的packages/drivers/driver-sql)加一条提示:改动IDataDriver契约或SqlDriver受保护成员时,同步检查cloud/packages/driver-turso。