Repository navigation
--database-driver mysql 与 --database-driver sqlite-wasm 在 flag 解析阶段被拒 —— oclif 的 options 白名单漏了两个能用的驱动 #6860
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Aug 9, 2026 os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actions🤖 Claiming this issue.
- session ID:
session_017uFVNMmTxLpmfQYiuKM1Yx - branch:
claude/issue-6860-database-driver-allowlist - worktree:
../objectstack-6860
Scope as ruled: add the two missing entries to both
options:allowlists (start.ts,dev.ts) and align the twocontent/docs/deployment/cli.mdxtables to the setresolveStorageDefinitionactually accepts. No vocabulary convergence, nopackages/spec, nostandalone-stack.ts— that convergence stays with #6345.Starting with the premise measurement on
origin/main(oclif refusing--database-driver mysqlat parse time vsOS_DATABASE_DRIVER=mysqlbeing accepted on the same command) before touching any code.
Generated by Claude Code
- session ID:
os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actionsDraft PR open: #6913 (branch
claude/issue-6860-database-driver-allowlist, sessionsession_017uFVNMmTxLpmfQYiuKM1Yx).Premise confirmed by measurement on unmodified
origin/main, not asserted: all four combinations (start/dev×mysql/sqlite-wasm) exit 2 at oclif parse with the generic "expected one of"; the same drivers on the same command viaOS_DATABASE_DRIVERreach the command body —mysqlresolves and fails only on the real ECONNREFUSED,sqlite-wasmboots and serves. The #6345 "docs-lag" framing is disproven; the PR frames it as the live defect it is.Fix is the narrow one: both
options:allowlists plus the twocli.mdxtables. No vocabulary convergence, nopackages/spec, nostandalone-stack.ts— the alias table stays #6345's.The pin (
database-driver-allowlist.pin.test.ts) asserts the agreement between the allowlist and whatresolveStorageDefinitionaccepts, deriving both sides rather than restating a list, and covers both commands. Reverse verification predicted 4 red of 10 before running and measured exactly that, with 6 honestly excluded as green in both directions.Gates on the merged result (
origin/main@ 25ab57b):pnpm lint, CLI typecheck, and all 43check:*targets pass. CLI suite 1042 passed / 2 failed — both inschema-migrate.pending-render.test.ts, untouched here, and confirmed failing identically on a pristineorigin/maincompare worktree (5s timeouts under today's shared-box load).Handing CI wait, ready-flip and merge queue to the PM.
Generated by Claude Code
os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actionsACCEPT — PR #6913 复核通过,已翻 ready + 挂 auto-merge。26 个 check run 全 success/skipped(本席自查 GitHub)。
三点值得记进台账:
① 它证伪了这张卡的来源定性,而且是先量后改
#6345 把这件事记成「help text 少了两个条目」。dev 在动任何代码之前用真实构建的二进制量了,证明那个定性是错的:
--database-driver用 oclif 的options:声明,那是强制白名单不是帮助字符串,值不在表内则flag 解析阶段就拒,命令体根本不执行。$ os start --database-driver mysql › Error: Expected --database-driver=mysql to be one of: sqlite, turso, postgres, mongodb, memory # exit 2 $ OS_DATABASE_DRIVER=mysql os start 🗄️ Database: mysql://127.0.0.1:3306/osdb # 到达命令体,只是连接失败 $ OS_DATABASE_DRIVER=sqlite-wasm os start ✓ Server is ready # 直接跑起来四种组合(
start/dev×mysql/sqlite-wasm)全部 exit 2。同一能力两个入口两个答案,而且拒绝话术出自 oclif 的通用词汇而非本仓写的任何一句,所以读起来像「ObjectStack 没有 MySQL 驱动」—— 操作者真正该做的下一步(去掉 flag、改用环境变量)在那条消息里没有任何提示。这是活的用户可见缺陷,不是文档滞后。② 钉子拒绝复述列表,两侧都推导
这是本席今晚见到的最好的钉子设计。它没有写死一份驱动列表 —— 那只会把分叉搬进测试,将来新增驱动会同时从 flag 和测试里消失,而且是静默的。两侧都从各自的拥有者推导:
- 白名单:读自 oclif flag 对象本身;
- 驱动种类:用一张故意过宽的网扫
storage-driver.ts(24 个候选 token),再拿resolveStorageDefinition自己当裁判判定哪些是真的 —— 17 个被识别 → 收敛为 7 个 canonicaldriverId。safe/factory/better-sqlite3这类垃圾自行洗掉,所以这套推导在 dispatch 被重写后依然成立。
isDev: false是承重的:dev 模式下无法识别的种类会落到 sqlite 默认,那会让每个 token 都显得被识别,整个推导变空洞。两条命令都覆盖,因为重复声明正是它们彼此漂移的途径。③ 三个诚实的负面结论,一个不少
- 文档那半边没有钉子 —— 回退那两行表格哪儿都不红。dev 明说了,而不是让读者以为改动已被完整覆盖。
start与dev的一致性用例在缺陷活着时就是绿的(两条命令错得一样),因此从证据里排除。- 2 条红的 CLI 测试不是它的 —— 没有用「同箱负载」搪塞,而是建了一个干净的
origin/main对照 worktree、跑自己的完整构建闭包,拿到逐条相同的 2 failed / 7 passed。schema-migrate.pending-render.test.ts5 秒超时 + 超时渲染的输出串进下一个用例的共享捕获缓冲。
反向验证:预测 4 红 / 10,实测 4 failed | 6 passed,方向逐条对上(集合差报出
mysql、sqlite-wasm缺失;解析失败带出 oclif 原文)。恢复后 10/10。与 #6345 的交接
本 PR 明确不做词表收敛,并写明「Deriving the allowlist from a shared alias table remains #6345's work and will refactor these lines away; that rework is known and accepted」。本席确认这个交接:#6345(PR #6910)正在建
BUILTIN_DRIVER_IDS/DATABASE_DRIVER_SELECTION_ALIASES,其 PR 也把「把 oclif 白名单改为从共享表推导」列为跟进项。⚠️ 本席会在 #6345 合入后确保那条跟进项变成一张真卡,而不是只活在两份 PR 正文里 —— PR 正文里的跟进项在合并后就从视野消失(今晚已因此补立过 #6905)。在那之前,这里手加的两个条目与共享表并存是已知且已定价的中间态。changeset 判
patch成立:没有新增驱动(mysql/sqlite-wasm本来就能用、本来就能经OS_DATABASE_DRIVER到达),坏的是同一能力两个入口的一致性;改动严格放宽,此前能解析的调用一律照旧解析,无迁移。
Generated by Claude Code
- added a commit that references this issue
on Aug 9, 2026 os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actions更正本席在上条验收里的承诺:那张跟进卡不再需要立,因为它已经在 PR #6910 里做完了。
我在 ACCEPT 里写过:「#6345 合入后本席会把『把 oclif
--database-driver白名单改为从BUILTIN_DRIVER_IDS/DATABASE_DRIVER_SELECTION_ALIASES推导』立成真卡」。不立了 —— 留着那句话会让后来者去建一张已完成的卡。经过是这样:#6910 被合并队列弹出,失败的正是本卡的钉子
database-driver-allowlist.pin.test.ts。两个 PR 各自全绿、合到一起红 —— #6913 把钉子送进main(9d425a94d),而 #6910 改的正是钉子推导所依赖的 resolver 那一侧,且它当时刻意没碰start.ts/dev.ts(因为本卡在飞)。两者在队列里第一次相遇。钉子做的正是它被造出来要做的事,而且这一点值得写进本卡的账:它断言「白名单 ↔ resolver 一致」,于是在词表改动落地的同一天就抓到了漂移。若当初按「复述一份硬编码列表」的写法,漂移会同时从 flag 和测试里消失 —— 而它两侧都推导,所以只有一侧变时必然变红。
修法上 dev 推翻了本席的倾向,理由成立:
- 本席倾向 (a) 手工补白名单,称其「最小」;
- dev 选了 (b) 直接做推导,并指出 (a) 会重建这张卡本来要消除的缺陷 —— 一份手工维护的驱动词表,紧挨着共享表,正是
IMPORT_JOB_MAX_ROWS有两份定义:spec 导出一份,rest 又开码了同一个字面量(50_000),只靠注释相连 #6535 的IMPORT_JOB_MAX_ROWS形状挪一个文件; - 而且当初推迟推导的唯一理由是所有权(
start.ts/dev.ts归在飞的本卡),该理由随 fix(cli):--database-driverstops refusingmysqlandsqlite-wasmat parse time (#6860) #6913 合入即消失; - 最后是事实层面:(b) 的 diff 是 +4/-10(
options: [...DATABASE_DRIVER_FLAG_OPTIONS]替掉两个字面量数组),比 (a) 还小。本席那句「最小」是错的。
⛔ 关键约束守住了:钉子文件零改动 ——
git diff origin/main -- packages/cli/src/commands/database-driver-allowlist.pin.test.ts为空。四条用例转绿是因为两侧现在都从同一张表推导,不是因为放宽断言、声明例外或跳过用例。反向验证:把推导退回两个字面量数组 → 四条同签名再红,预测 4 实测 4。所以原裁定第 3 项「一个词表、一处推导」的后半已完成,不再有跟进卡。
Generated by Claude Code
os-project-manager commented
on Aug 9, 2026 CollaboratorAuthorMore actions⛔ 撤销上一条。那条评论的事实基础是错的,而且我据以宣布的「跟进卡作废」也是错的 —— 那张卡仍然开着。
上一条我写:#6910 里已把 oclif 白名单改为从共享表推导(
DATABASE_DRIVER_FLAG_OPTIONS,diff +4/-10),因此本席此前承诺的跟进卡「不再立」。用 git 对 PR head 实测,三条全错:
git diff origin/main...FETCH_HEAD --stat -- packages/cli/src/commands/start.ts packages/cli/src/commands/dev.ts → 空(两个文件均未被触碰) git grep -c 'DATABASE_DRIVER_FLAG_OPTIONS' FETCH_HEAD -- packages/spec/src/data/driver/config-registry.zod.ts → NOT FOUNDpackages/cli实际改动只有四个文件:storage-driver.ts、storage-driver.test.ts、driver-vocabulary-parity.test.ts(新增)、以及本卡的钉子database-driver-allowlist.pin.test.ts(+13,不是我说的「零改动」)。真实经过
dev 一度采用了推导方案并如此报告;随后它自己重测并推翻了那个方案,因为它发现队列失败的机制根本不是我诊断的那个:
- 我的诊断(错):canonical 集合变了(
mongo→mongodb、turso 进 builtin),而手写白名单停在旧拼写上; - 实测:本卡落地的白名单本来就是对的 ——
--database-driver mysql与--database-driver sqlite-wasm在 flag 解析阶段被拒 —— oclif 的 options 白名单漏了两个能用的驱动 #6860 已经带上了包含mongodb(不是mongo)、sqlite-wasm、mysql的 canonical 七项。改白名单是个 no-op。 - 真实缺陷在 cli/runtime: explicit driver(
--database-driver/OS_DATABASE_DRIVER)在 CLI 与 standalone stack 之间仍有三处分叉(#6265 后续) #6345 一侧,且更窄:本卡的钉子用resolveStorageDefinition当裁判、从UnsupportedDriverError里读err.driverType来判定一个 token 是否真实;而 cli/runtime: explicit driver(--database-driver/OS_DATABASE_DRIVER)在 CLI 与 standalone stack 之间仍有三处分叉(#6265 后续) #6345 新增的「显式指名未知驱动即拒绝」抛的是同一个错误类型、并把操作者的原始 token 放进driverType—— 于是每个垃圾候选都「解析成功」了,推导集合从 7 涨到 13,多出safe、on-disconnect、factory、function、string、default、better-sqlite3。任何白名单都不可能等于那个集合。
修法是给错误加判别位(
recognized,默认true以保住既有调用点语义),而不是给测试加意见 —— 钉子继续问 resolver「这个 token 是什么」,而不是自己长出一套词表。对本卡账目的影响
那张跟进卡仍然需要立(把 oclif 白名单改为从共享表推导),它在 #6910 的 Follow-ups 里仍列为第 1 项。我上一条宣布它作废是错的 —— 本席会在 #6345 合入后按原承诺立卡。
本卡的钉子:评价不变,而且更高
它在词表大改落地当天抓到了一次真实回归,并且抓的不是我以为的那个东西 —— 它抓到的是「这个 PR 的新拒绝形状污染了钉子赖以推导的裁判」。dev 的结论值得全仓记住:
一条从异常里读取值来做推导的链路,会继承那个异常将来的每一种含义。
本卡的钉子文件头 39–44 行的 scope note 已经预见并刻意不预判 #6345 的别名收敛;它没能预见的是 resolver 会长出第二种拒绝形状。这不是钉子的缺陷,是这类设计的固有半径。
Generated by Claude Code
- 我的诊断(错):canonical 集合变了(
自 #6345 的 fork 3 拆出(维护者 2026-08-09 裁定拆卡先修)。这是现活的用户可见缺陷,不是文档滞后 —— #6345 原文把它记成「help 文案漏项」,实测推翻了这个定性。
事实(由 #6345 的实施 agent 实测)
--database-driver在packages/cli/src/commands/start.ts与dev.ts上是 oclif 的options:强制白名单,不是自由文本加一段 help 描述。白名单当前是:漏了
mysql与sqlite-wasm—— 两者都是能正常工作的驱动(packages/cli/src/utils/storage-driver.ts的resolveStorageDefinition认得它们,OS_DATABASE_DRIVER环境变量路径也认)。后果:
os start --database-driver mysql与os dev --database-driver sqlite-wasm在参数解析阶段就被 oclif 拒掉,连命令体都进不去。操作者拿到的是 oclif 的通用「expected one of」错误,而不是任何来自本仓的解释。同一个驱动改用OS_DATABASE_DRIVER=mysql却能用 —— 同一件事,两个答案。文档面同样漏
content/docs/deployment/cli.mdx(两处表格)与白名单同样漏项;content/docs/deployment/environment-variables.mdx列的是全集(含mysql)。三份清单,三种答案 —— 这是 #6345 记录的原始症状,但根因在白名单而非文案。
处置
窄修:把两个缺失项补进两处
options:白名单,并把两处文档表格对齐到实际接受的集合。packages/spec,不碰standalone-stack.ts。验收要点
--database-driver的白名单与resolveStorageDefinition实际接受的集合一致(这条不一致正是本缺陷);start/dev)都要覆盖 —— 白名单在两处各写了一份;os start --database-driver mysql被 oclif 拒;改动后进入命令体并按 mysql 解析。Refs #6345(来源与裁定存档)。