feat(spec,cli): 活性闸门开始对注册表负责 —— 并治理 datasource(43 条判定,20 条无消费者)(#4487) - #4489
Merged
Merged
Conversation
…ype (#4487) `GOVERNED` was a hand-maintained list and nothing compared it against the registry it claims to cover. It governed 15 of 25 registered metadata types while reporting itself complete: a type in the other ten was authorable — served by /api/v1/meta/types/:type, editable in Studio — and was never asked who reads its properties, so an inert key on it was invisible to CI and its silence read as success. `datasource` was in that state for its entire life. #4410, #4465 and #4481 found six inert keys on it by hand, two security-shaped: `schemaMode` was dropped between record and connection spec, so a database ObjectStack must never run DDL against was constructed as `managed`; `ssl` stopped at the record, so a TLS block with a CA certificate configured nothing while looking identical to one that worked. The gate is now answerable to the registry. Every registered type must be in GOVERNED or in PENDING_GOVERNANCE with a reason and an issue; registering a type and forgetting the ledger fails CI with the entry to write. The reverse rots too and also fails — a PENDING_GOVERNANCE row for a type since governed claims a debt that no longer exists. `datasource` is now governed: 43 properties classified with evidence, and the result is the highest dead ratio of any governed type — 20 of 43 have no runtime consumer. `capabilities.*` (11): the engine gates pushdown on the runtime driver's own `supports.*` object, a non-overlapping vocabulary. `healthCheck.*` (3): nothing schedules a datasource probe. `retryPolicy.*` (4): no connect or query path retries. Plus `external.label` and `external.requirePermission`. One correction ships with this, and it is why the audit was worth doing. `capabilities.readOnly` reads as a safety switch and gates nothing — and two shipped prescriptions pointed authors at it: the externalSettingsUnknownKeyError guidance in datasource.zod.ts and the #4465 changeset's relocation table. Both now name `external.allowWrites: false`, the write gate the engine checks. The v17 release notes carried a matching false claim about `capabilities` gating pushdown; corrected here too. The CLI advisory lint picks the ledger up, so `os compile` warns an author who sets any of the 20. That needed `datasource` in TYPE_COLLECTIONS: coverage grows by marking entries authorWarn only WITHIN a type the lint already walks, and a governed type whose collection is unregistered has a correct ledger that warns nobody. Nine types remain ungoverned and are now enumerated rather than implied (#4488). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WsgTqRF58HsQYKLsrZ5pQY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 113 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4487
闸门自己有个盲区
check-liveness.mts的GOVERNED是一份手工维护的名单,从来没有任何东西拿它和它声称覆盖的注册表对过。结果:它治理 25 个已注册 metadata type 中的 15 个,同时报告自己完整。剩下 10 个是可授权的 ——
/api/v1/meta/types/:type提供它们,Studio 能编辑它们 —— 但从来没有被问过"谁读这个属性"。所以那些类型上的死键对 CI 不可见,而它们的沉默被读作成功。一份治理 15/25 却自报完整的名单,比一份承认缺口的名单更糟。
datasource在这个状态里待了它的整个生命周期。#4410 / #4465 / #4481 在它身上手工挖出 6 个死键,其中两个是安全形状的:schemaMode在记录→连接规格之间掉了,一个 ObjectStack 绝不能跑 DDL 的外部库被当作managed构建ssl停在记录层,一个带 CA 证书的 TLS 块配置了零,且和生效的那个长得一模一样闸门对此一言不发,因为这个类型不在名单上。
改了什么
1. 闸门对注册表负责(持久的那一半)
每个已注册类型必须在
GOVERNED里,或者在PENDING_GOVERNANCE里带一条理由和一个 issue 号。注册一个类型却忘了账本 → CI 失败,并把该写的条目告诉你。反方向也会烂,所以也失败:一个已经治理完的类型如果还留在
PENDING_GOVERNANCE里,就是在声称一笔不存在的债,让"还剩多少"变成谎话。和 orphan ledger row 同一种腐烂,方向相反。这是个棘轮,不是可以随便加的白名单。注释里写明了:为了让刚注册的类型变绿而加一条,正是这张表要暴露的失败本身;没有 issue 号的条目和"从没看过"无法区分。
负证明(silence must not read as success,这条闸门自己也得守):临时从
PENDING_GOVERNANCE删掉seed,闸门以 exit 1 失败并打印该类型 + 补救指引;恢复后 exit 0。2. datasource 治理完毕
liveness/datasource.json,43 条属性全部带证据分类。结果是所有被治理类型中最高的 dead 占比:43 中有 20 条没有 runtime 消费者。capabilities.*(11)supports.*对象(autonumber/batchSchemaSync/queryDateGranularity)—— 另一套机制,词汇表和这个块零重叠。having-filter.ts:13说得很直白:"SQL pushdown can come later behind a driver capability flag"healthCheck.*(3)ping()检查的retryPolicy.*(4)external.label/external.requirePermission3. 一处更正 —— 这是这次审计值得做的理由
capabilities.readOnly读起来像安全开关,实际不守任何东西。而有两处已发布的处方把作者指向它:datasource.zod.ts的externalSettingsUnknownKeyError:"…orcapabilities.readOnlyto describe the driver"readOnly→capabilities: { readOnly: true }两处现在都指向
external.allowWrites: false—— ObjectQL 引擎真正检查的写入门(engine.ts:620)。照旧建议做的作者,相信自己把一个数据源标成了不可写,而并没有。这正是我在同一个文件里为
belongsInConfig写过的那段失败:"把一个犯了可恢复错误的作者,用平台的权威指向一个同样错误再次沉默的位置。" 我在隔壁一个字段上做了同样的事,而且是在一个全部意义就是安全的属性上。v17 release notes 里还有一句配套的假声明 —— 说未注册的
capabilities键会让引擎停止下推、改在内存里算 —— 一并更正。4. 作者警告闭环
CLI 建议 lint 是账本驱动的,所以
os compile现在会警告设置了那 20 条中任何一条的作者。这需要账本之外的一行:把datasource加进TYPE_COLLECTIONS。值得记一笔的区别:覆盖率靠标
authorWarn增长,但仅限于 lint 已经在走的类型内部。一个新治理的类型如果没登记 collection,账本正确却警告不到任何人 —— 这正是 lint 存在要防的形状。三个坑,每个都差点造成错判
healthCheck全仓 20 处命中,没有一处属于 datasource(插件健康监控、AI model registry、connector、StartupOrchestratorOptions)。retryPolicy更险 —— 它在hook和job上确实被强制执行。分辨靠形状:hook.retryPolicy用backoffMs,datasource 用baseDelayMs/maxDelayMs/backoffMultiplier,没有代码同时读两种拼写。DatasourcePreview把pool/ssl/retryPolicy/healthCheck渲染成面板 —— 一条都没有被当作证据。feat(spec)!: 退休 datasource.readReplicas —— 声明了、strict 了、刚被加了校验,但没有任何东西打开过副本连接 (#4468) #4481 是最新的教训:readReplicas唯一的"消费者"是一枚 pill。z.record是账本盲区。datasource.config是开放 record,walk 到此为止,per-driver 键由data/driver/*.zod.ts管。这条显式记进了config的 note,而不是默默跳过 —— 沉默的盲区和不存在的盲区在文件里长得一样。一件我没有做的事
liveness/README.md的 per-type 计数表有几行陈旧。我试着机械重算,产生了两处回归(query的 7 个 experimental 来自 describe marker 不是账本条目;webhook行是手工标注的),及时发现并回滚了。原因是有两种口径在打架:README 自带的 python 片段数账本 JSON,闸门的
--json还解析 marker 并对children不同下钻。修它需要先决定这张表是哪个口径,那是一个决定,不该夹在这个 PR 里顺手做。所以我只加了datasource行,并把这个坑连同"我踩过"一起写进了 README 和 #4488。验证
@objectstack/spec— 285 files / 7248 tests passed@objectstack/cli— 67 files / 689 tests passed(新增 3 条 datasource lint pin test:死块会警告、capabilities.readOnly的 hint 必须点名allowWrites、只设活属性时保持沉默)PENDING_GOVERNANCE→ exit 1 且打印补救指引;恢复 → exit 0闸门输出的新一行:
后续
#4488 —— 剩余 9 个类型的 worklist,
PENDING_GOVERNANCE每条都指向它。里面写了 datasource 这次用的方法(**先找类型的"边界结构"**是效率最高的一步 ——ConnectableDatasource+DatasourceConnectionSpec一次判掉了 20 条里的大部分)、上面三个坑、README 计数口径那件事,以及一条明确要求:不要为了绿而批量填live,橡皮图章账本比没有账本更糟。那 20 条死属性本身是 ADR-0049 的退休 worklist,不在本 PR 范围 —— 账本的职责是记录判定,
authorWarn负责告诉作者;退休是每个簇各自的决定,像 #4481 那样单独走。Generated by Claude Code