Repository navigation
活性账本覆盖 worklist:9 个已注册 metadata type 仍未治理(#4487 建立闸门后的剩余债务) #4488
Description
#4487 让活性闸门对注册表负责:每个已注册的 metadata type 必须在 GOVERNED 里,或者在 PENDING_GOVERNANCE 里带一条理由和一个 issue 号。这个 issue 就是那些条目指向的地方。
datasource 已经在 #4487 里治理完毕(43 条判定,20 条 dead)。剩 9 个:
| type | 备注 |
|---|---|
app |
#4001 的 app step 退休了 7 个死键却没有播种账本,AppSchema 其余部分至今未分类 |
book |
ADR-0046 文档骨架,从未做过属性审计 |
doc |
ADR-0046 扁平 Markdown 文档,面小,未审计 |
email_template |
未审计 |
job |
未审计。注意 job.retryPolicy 是被强制执行的(runtime/src/app-plugin.ts:791),不要照搬 datasource 的结论 |
mapping |
#2611 可复用导入映射,未审计 |
seed |
发布时应用的初始数据,未审计 |
translation |
未审计 |
validation |
ValidationRuleSchema 承载 ADR-0020 记录状态机,判错代价高,未审计 |
怎么做一个(datasource 那次的方法,复述一遍免得重新发明)
npx tsx scripts/liveness/check-liveness.mts --dump <type>出属性清单- 先找那个类型的"边界结构",这是效率最高的一步。datasource 的边界是
ConnectableDatasource和DatasourceConnectionSpec—— 不在这两个 interface 上的块,根本到不了驱动,一次性判掉了 20 条里的大部分。每个类型都有类似的东西(编译后的 stack、传给执行器的 config、序列化到 DB 的行) - 逐条闭合调用图,
live的写evidence(file.ts:line),全部写verifiedAt - dead 且有误导性的加
authorWarn+authorHint;authorHint必须指向真正被强制执行的机制,否则只是把作者的困惑挪个地方 - 加进
GOVERNED,从PENDING_GOVERNANCE删掉(闸门两个方向都会失败,删漏了会报 stale) - 如果这个类型要 author 警告,还要把它加进 CLI 的
TYPE_COLLECTIONS(packages/cli/src/utils/lint-liveness-properties.ts)。账本正确但没人被警告是最糟的中间态
三个坑,每一个都在 #4487 里差点造成错判
1. 同名不同类型。 healthCheck 在仓库里有 20 处命中,没有一处属于 datasource —— 全是插件健康监控、AI model registry、integration connector、StartupOrchestratorOptions。retryPolicy 更险:它在 hook 和 job 上确实被强制执行,所以 grep 看起来是活的。分辨方法是看形状:hook.retryPolicy 用 backoffMs,datasource 用 baseDelayMs/maxDelayMs/backoffMultiplier —— 没有任何代码同时读两种拼写,这个不一致本身就是"没人读"的证据。
2. preview renderer 不算消费者。 liveness/README.md 有专门一节。#4481 是最新的例子:readReplicas 在两个仓库里唯一的"消费者"是 objectui 的一枚 pill。datasource 的 retryPolicy / healthCheck 也被 DatasourcePreview 渲染成面板 —— 一条都没有被当作证据。
3. z.record 是账本的盲区。 datasource.config 是开放 record,walk 到此为止,所以作者真正写的键(host、port、filename)由 data/driver/*.zod.ts 的 per-driver schema 管,不由账本管。这条显式记在了 config 的 note 里而不是默默跳过。其他类型碰到 z.record 请照做 —— 沉默的盲区和不存在的盲区在文件里长得一模一样。
顺带一件独立的小事
liveness/README.md 的 per-type 计数表有几行是陈旧的,#4487 故意没有批量修。原因是有两种计数口径会打架:README 自带的 python 片段数账本 JSON 条目,闸门的 --json 还会解析 describe() marker 并对 children 做不同下钻;而 query 和 webhook 两行是手工标注、超出任何一种口径的(见它们各自的 note)。用错口径机械重写会静默抹掉那些标注 —— 写 #4487 时试过一次,产生了两处回归才被发现。
修它的正确方式是先决定这张表到底是哪个口径,并把这句话写进 README,然后再重算。不难,但需要一个决定,不适合夹在别的 PR 里顺手做。
验收标准
PENDING_GOVERNANCE清空- 每个类型的账本每条属性有判定 +
verifiedAt;live有指向真实 runtime reader 的evidence - 需要作者警告的类型都进了
TYPE_COLLECTIONS - 每个类型的
z.record/ 开放形状边界显式记录 - README per-type 表口径确定并重算
- 不要为了绿而批量填
live—— 一个橡皮图章账本比没有账本更糟,它把"没人看过"伪装成"看过且干净"
参考
- datasource 是注册的 metadata type,却不在 liveness 账本的 GOVERNED 里 —— 两个 PR 手工挖出 6 个死键,正是因为没有闸门看着它 #4487 —— 建立闸门 + 治理 datasource(方法论范本)
- datasource.config 至今无人校验:驱动 configSchema 是声明但完全惰性的(ADR-0049 enforce-or-remove,#4001 收尾发现) #4410 / feat(spec,service-datasource): datasource.config 按驱动契约校验 (#4410) #4465 / feat(spec)!: 退休 datasource.readReplicas —— 声明了、strict 了、刚被加了校验,但没有任何东西打开过副本连接 (#4468) #4481 —— 那 6 个手工挖出来的死键,本闸门存在的理由
packages/spec/liveness/README.md—— 判定方法论- ADR-0049 enforce-or-remove / ADR-0078 no-silently-inert-metadata / ADR-0054 prove-it-runs
未指派 —— 可以按类型拆开并行做,认领一个就在这条 issue 下说一声,避免撞车。
Activity
feat(spec,cli): the liveness gate governs every registered metadata t…
os-zhuang commentedon Aug 1, 2026
认领:全部 9 个类型一起做(app, book, doc, email_template, job, mapping, seed, translation, validation),外加 README 计数口径的决定。已自派,其他 agent 请勿重复认领单个类型。
按 #4487 的方法逐类型闭合调用图;
job.retryPolicy的强制执行点(runtime/src/app-plugin.ts:791)已记录在案,不会照搬 datasource 的结论。Generated by Claude Code
feat(spec,cli): govern the 9 remaining metadata types — liveness cove…
feat(spec)!: retire datasource retryPolicy / healthCheck / external i…
feat(spec)!: 清空剩余六条 authorWarn 死键 —— book ×2 / job.id / translation.v…
chore(spec): regenerate baselines after merging origin/main (spec 车道接力)
feat(spec)!: ViewItemSchema 拆成授权门 + wire 变体,并让 wire 的开放递归生效 (objectst…
feat(spec): the liveness state table's counts become a generated arti…
docs(spec): book.json's liveness header names the dead row, not a pai…
docs(spec): book.json's liveness header names the dead row, not a pai…
spec: declaresCollection reads a pipe's authorable side, so a preproc…