Skip to content

Commit 9c93465

Browse files
os-zhuangclaude
andauthored
fix(spec,docs): check-react-blocks-conformance 比的是两份声明,不是声明↔实现 (#4472) (#4491)
它的文件头原话是"confirms the objectui components ACTUALLY implement the props the spec protocol declares"。它做不到,也从来没做过。它 diff 的两边都是**声明**:左边是 spec zod schema 的 props,右边是 objectui 注册表配置声明的 inputs——由 `manifestFromConfigs` 原样抄进 sdui.manifest.json。整条链路里没有渲染器。所以一个 **两边都声明、没有任何东西读**的 prop,在这道门禁眼里是完美一致。 #4413 就是代价:四个 record:* 块发布了没人读的 objectName/recordId,在 kind:'react' 页面上渲染成 "bind a record to preview" 占位符,而 baseline 里 `{ "frontendOnly": [], "missing": false }` 稳稳绿了整个缺陷存续期。最后是人肉读 objectui 渲染器发现的。**一道报绿的假门禁比没有门禁更危险**——没有门禁时人会去核对。 Prime Directive #10(declared ≠ enforced)落在本该抓 #10 的东西自己身上,和 #1475 「spec 声明 9 种校验规则、执行器只认 3 种」同形。 - 改名到它真正做的事,名字和文件头一起改(名字本身是误信任的一部分): check-react-blocks-declaration-parity.ts、react-declaration-parity.baseline.json, baseline 键 frontendOnly → registryOnly(是"注册表声明了",不是"前端实现了")。 - 作用域说明随**每一次**输出走,包括绿的那次。对这道门禁形成判断的人读的是 CI 日志, 不是文件头。 - 它现在真的 gate。gen-sdui-manifest.sh 调用时没传 --strict,还把退出码吞进一个 ⚠, 所以连它看得见的那部分也只是被记录、从未被拦住(#4472 次要发现 1)。ratchet 只对 相对 baseline 的**新增**分歧开火,所以失败必定是一次有意的注册表改动。 - 声明由测试钉住。check-react-blocks-declaration-parity.test.ts 断言它能看见的两个 方向、caveat 确实被打印、以及那句"实现"声明不会回来——Prime Directive #10 可执行的 那一半。 它看得见的东西没变,也仍然值得留着:spec-only(palette 缺口,软信号)、registry-only (未文档化的扩展,被 ratchet)、missing(没注册/非 public)。看不见的只有一类:两边都 声明了,没人读。 渲染路径上的证据只能从渲染路径上取,那是 objectui 那侧。那里的 public-block-binding-reach.test.tsx 把每个声明了 objectName 的 public block 挂在一个 会记录调用的 dataSource 下,断言绑定确实到达了数据层;首跑就把五个绑上的和三个没绑上 的分开,并暴露两个同形真缺陷(objectui#3144)——这恰好证明这类证据在 spec 侧从来取不到。 ADR-0082 带 addendum;2026-06 那份 audit 顶上加了更正横幅,指出那句让整件事看起来安全的 假设("the component reads its full config from the spec schema at render")是预期, 从未被测量。 Closes #4472 Claude-Session: https://claude.ai/code/session_01S3cP1eY1novcNhQEDBrSZD Co-authored-by: Claude <noreply@anthropic.com>
1 parent f3141d8 commit 9c93465

13 files changed

Lines changed: 573 additions & 231 deletions
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
`check:react-conformance``check:react-declaration-parity` — the gate compares two declarations, and said it compared a declaration to an implementation.
6+
7+
Its header opened by claiming it "confirms the objectui components **ACTUALLY implement**
8+
the props the spec protocol declares". It never could. Both sides of its diff are
9+
declarations: the spec zod schema's props on the left, and on the right the `inputs` the
10+
objectui *registry config* declares — copied verbatim into `sdui.manifest.json` by
11+
`manifestFromConfigs`. No renderer appears anywhere in the chain. So a prop **both sides
12+
declare and nothing reads** is, to this gate, perfect agreement.
13+
14+
That is not hypothetical. #4413's four blocks (`record:details` / `record:highlights` /
15+
`record:related_list` / `record:path`) published `objectName`/`recordId` that no renderer
16+
read, rendered a "bind a record to preview" placeholder on a `kind:'react'` page, and sat
17+
behind `{ "frontendOnly": [], "missing": false }` in the committed baseline for the whole
18+
life of the defect. A human reading the objectui renderers found it. A gate reporting
19+
green on a promise it cannot keep is worse than no gate — without one, someone checks by
20+
hand.
21+
22+
Prime Directive #10 (declared ≠ enforced), landing on the thing whose job is to catch it.
23+
Same shape as #1475's "spec declares 9 validation rules, the executor honors 3".
24+
25+
- **Renamed to what it does**, name and header together, because the name was load-bearing
26+
in the misreading: `check-react-blocks-declaration-parity.ts`,
27+
`react-declaration-parity.baseline.json`, and `frontendOnly``registryOnly` in the
28+
baseline ("the registry *declared* it", not "the frontend *implements* it").
29+
- **The scope caveat is emitted on every run, clean ones included.** Whoever forms a
30+
belief about this gate is reading a CI log, not a source header.
31+
- **It actually gates now.** `gen-sdui-manifest.sh` ran it without `--strict` and swallowed
32+
the exit code behind a ``, so even the divergence it *could* see was recorded and never
33+
stopped (#4472 secondary finding 1). The ratchet fires only on divergence new since the
34+
accepted baseline, so a failure is always a deliberate registry change.
35+
- **The claim is pinned by a test.** `check-react-blocks-declaration-parity.test.ts`
36+
asserts both directions of what the gate can see, that the caveat rides along, and that
37+
the implementation claim does not come back.
38+
39+
What it sees is unchanged and still worth having — `spec-only` (palette gap, soft),
40+
`registry-only` (undocumented extension, ratcheted), `missing` (not registered / not
41+
public). Exactly one class is invisible: both sides declare it, nothing reads it.
42+
43+
Evidence about the render path has to come from the render path, which is objectui's side.
44+
`public-block-binding-reach.test.tsx` there mounts every public block declaring an
45+
`objectName` under a recording `dataSource` and asserts the binding reaches it; its first
46+
run separated five bound blocks from three unbound and surfaced two real defects of the
47+
same shape (objectui#3144) — the confirmation this evidence was never obtainable here.
48+
ADR-0082 carries the addendum; the 2026-06 audit carries a correction banner over the
49+
assumption that carried the mistake ("the component reads its full config from the spec
50+
schema at render" — an expectation, never measured).

AGENTS.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,20 @@ removals" this way while writing this section; `check:generated` now prints this
342342
inline when that gate is the one failing.)
343343

344344
`check:liveness`, `check:empty-state`, `check:skill-examples`,
345-
`check:react-conformance` and `check:exported-any` are pure checks with no generator — a
346-
failure there is a real finding to fix, not an artifact to regenerate. `check:generated`
347-
names them as deliberately not run, so its "all up to date" never reads as "everything
348-
passed".
345+
`check:react-declaration-parity` and `check:exported-any` are pure checks with no
346+
generator — a failure there is a real finding to fix, not an artifact to regenerate.
347+
`check:generated` names them as deliberately not run, so its "all up to date" never reads
348+
as "everything passed".
349+
350+
⚠️ **`check:react-declaration-parity` compares two DECLARATIONS, not a declaration against
351+
an implementation.** Left: the props a block's spec zod schema declares. Right: the inputs
352+
the objectui *registry config* declares. Both are declarations — `manifestFromConfigs`
353+
copies `config.inputs` verbatim — so a prop **both sides declare and no renderer reads**
354+
is, to this gate, perfect agreement. It was named `check:react-conformance` and opened by
355+
claiming it confirmed the components "ACTUALLY implement" the spec props; it never could,
356+
and #4413 shipped four dead blocks straight through a green run of it. Renamed and
357+
re-scoped in #4472. The gate is still worth having (`spec-only`, `registry-only` and
358+
`missing` are real signals) — just don't read it as proof anything renders.
349359

350360
`check:exported-any` is the one of those that also reads the built `dist/*.d.ts`, so the
351361
stale-`dist` caveat above applies to it too. It asks the other half of the

0 commit comments

Comments
 (0)