Skip to content

check-driver-conformance 的 audit 路径对「发现零个 driver」报 OK — 零发现该是失败,且唯一的守卫硬编码了 driver-sql #4646

Description

@os-zhuang

包:scripts/check-driver-conformance.mjs(#4363 引入的 driver conformance gate)
核实基线:main @ 0a936ea,已实测复现(下附命令与输出)
关联:#4645(driver 目录重组 packages/plugins/driver-* → packages/drivers/*)——这是那次重组的前置项,但本身独立成立
PR:#4648

摘要

gate 的发现根写死在 packages/plugins,而 listDir 吞掉 ENOENT 返回 []。audit 路径在发现零个 driver 时不报错,打印 OK — 0 covered cell(s) 并 exit 0。

先把范围说准,避免把它讲得比实际严重:今天 CI 拦得住。lint.yml:304 跑的是 pnpm check:driver-conformance,而 package.json:50 把它定义成 --self-test && audit,self-test 里有一条 driver 发现断言会先失败。问题在于守卫只长在 self-test 上,audit 自己没有——而 audit 是脚本 header 第 21 行明确文档化的独立用法。

实测

把 DRIVERS_DIR 指向一个不存在的目录(即「目录改名了但常量没跟上」),两条路径:

$ node check-driver-conformance.mjs

driver conformance matrix (0 drivers x 5 case-sets)

  driver    FILTER_LOGIC  TEMPORAL  TEMPORAL_TIME  PAGINATION  PAGINATION_UNORDERED

check-driver-conformance: OK — 0 covered cell(s), 0 in the DEBT ledger, 0 exempt.

exit=0        ← 覆盖零个 driver,报 OK
$ node check-driver-conformance.mjs --self-test
  x self-test: discovers driver packages from disk

check-driver-conformance --self-test: 1 failure(s).

exit=1        ← 这条拦住了

三条不变量为什么会全部空过

audit() 里:

errors 为空 → report() 走到 :318 打印 OK。空台账是预期的稳态,所以 RECONCILED 这条兜底在正常状态下恒为空——它挡不住这个洞。

根因:两条轴的不对称

更正:本 issue 早前版本说「CASE_SETS_DIR 也没有任何守卫,该一并加固」——那是错的,实测它有守卫。把 CASE_SETS_DIR 指向不存在的目录:

  x CLASSIFIED: CASE_SETS names FILTER_LOGIC_CASES, which filter-logic-conformance.ts no longer exports.
  x CLASSIFIED: CASE_SETS names TEMPORAL_CASES, ...
  ...(5 条,每个 case-set 一条)
check-driver-conformance: 5 problem(s).   exit=1

给它再加一层是冗余的。

两条轴之所以一条会烂一条不会,差别在有没有一份声明的预期可供反向对账:

轴 声明的预期 反向对账 目录失效时
case-set CASE_SETS(脚本内 5 条) CLASSIFIED 第二个方向 5 条 error
driver 无——纯磁盘发现 RECONCILED 遍历 LEDGER,而它按设计恒空 静默 OK

driver 轴不能靠「补一份清单」来修——那正好违反脚本 :145 的 /** Driver packages, from disk — never a hardcoded list. */。能声明的只有一条:零不是有效的发现结果。

为什么仍然要修

  1. 文档化的用法会给出假绿。 header :21-22 把两条命令并列列出,直接跑 audit 是被鼓励的(本地迭代时谁都不想每次跑 self-test)。它对零发现返回 OK。
  2. 唯一的守卫硬编码了 driver 名和数量。 selfTest() :375:
    expect('discovers driver packages from disk', drivers.length >= 3 && drivers.includes('driver-sql'));
    而同一份脚本 :145 的注释是 /** Driver packages, from disk — never a hardcoded list. */。守卫违反了被守卫者的设计原则;>= 3 还会随 driver 增加而静默失去意义(现在 4 个,迁入 turso 后 5 个)。
  3. 失败信息不指向真因。 discovers driver packages from disk 不提 DRIVERS_DIR、不说路径已失效。driver-turso 住在闭源 cloud 仓,却是开源 runtime 点名偏好的默认 driver — 建议把核心迁回本仓 #4645 的重组正是要动这个常量,届时踩中的人拿到的是一句不知所云的自检失败。
  4. 这正是该脚本 header 自己点名要避免的形状——「a verifier that reports success while degrading(route-ownership rule 3)」。它把这条标准用在了被检查对象上,没用在自己身上。

修法(#4648 已实施)

audit() 增加第四条不变量 DISCOVERED,检查体抽成 discoveredErrors(drivers) 以便 self-test 驱动不变量本身而非其替身;self-test 里的 driver-sql / >= 3 硬编码随之删除。失败信息带上实际搜索的目录:

x DISCOVERED: no driver package found under packages/drivers/. Either these
  packages moved and DRIVERS_DIR is stale, or they are gone. Every other
  invariant iterates the discovered set, so a zero-driver run reports OK having
  checked nothing — it fails here instead.

case-set 轴按上面的实测结论不动。

不做的代价

小,但方向错。gate 存在的理由就是「把缺失变成失败」;它自己在最基础的一格上把缺失变成了 OK。#4645 的重组会实际触发这一格,而那时诊断成本落在一个正在做别的事的人头上。

Activity

  1. self-assigned this
    on Aug 2, 2026
  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    🔒 CLAIM
    session: session_01VTj56JLhVk585TA8Ez3yGK
    branch: claude/turso-driver-evaluation-beoewn
    worktree: objectstack-turso-eval

    分支名不走 claude/issue-<n>-<slug> 约定,是因为本会话被指派了固定分支 claude/turso-driver-evaluation-beoewn(turso driver 归属评估,本 issue 是其派生的前置修复)。

    按正文的方案实施:给 audit() 加 DISCOVERED 不变量,self-test 改为断言该不变量、去掉 driver-sql / >= 3 硬编码,并给 CASE_SETS_DIR 同等待遇。


    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

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions