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 的重组会实际触发这一格,而那时诊断成本落在一个正在做别的事的人头上。

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