Skip to content

registry-inputs-spec-parity 对 array/object 类 key 降级成「只比名字」——ComponentInput 没有承载成员形状的槽位,page:header.actions 因此漂移了整整一个契约周期 #8067

Description

@os-steve

事实

apps/console/src/__tests__/registry-inputs-spec-parity.test.ts 自述,逐字:

LIMIT — worth knowing before trusting a pass. This gate compares TOP-LEVEL KEY NAMES and nothing else. Two things it therefore cannot see, both real and both filed:

  • member shapes. An inputs entry of type array/object declares no member shape (ComponentInput has no slot for one), so a drifted key INSIDE an array element or nested object is invisible here …

本卡是这两条 LIMIT 中第一条的卡。(第二条「types NARROWER than the contract」是刻意不 gate 的,见闸门的 ARM DIRECTION 一节与 #4971,⛔ 不在本卡范围。)

ComponentInput 定义在 packages/types/src/base.ts:528,经 @object-ui/core 导出。它有 type: 'array',但没有任何字段说「数组里装的是什么」。

⇒ 闸门虽然从 ComponentPropsMap[type] 的运行时 shape 取期望(spec 侧是知道元素类型的,zod 携带它),但因为 inputs 侧无法表达,比较只能退化到顶层 key 名字。

已发生的代价:page:header.actions

侧 声明
@objectstack/spec actions: z.array(z.string()).describe("Action IDs to show in header")
渲染器实际读取 把成员当 ActionDef 对象:rawHeaderActions.filter(a => actionRendersAt(a, 'record_header') …)
本闸门 两侧都有 actions 这个 key ⇒ 绿

漂移从这个 key 存在起一直到 objectstack#11592(2026-08-24 立卡)才被人注意到,而且settle 它的是维护者裁决,不是任何测试(2026-08-25「全部同意」推荐 B;#6252 / #7182 option C)。

⚠️ 今天修好之后,「这是 id」这个事实仍然只活在散文里 —— packages/components/src/renderers/layout/containers.tsx:2032:

{ name: 'actions', type: 'array',
  description: "Action IDS — the names of actions declared on the object's own metadata — …" }

⇒ 机器读不到。这正是本仓反复在打的 declared-in-prose / unenforced 模式。

下游代价(说明它为什么值得修,⛔ 不是本卡要做的事)

消费方 objectstack-ai/hotcrm 的 os lint 是整个系统里唯一看见形状分歧的东西(component-props-invalid 按 spec schema 校验,含形状,16 条)。但那是唯一没有权威改渲染器的一侧,于是 16 条正确报警被当成噪声,写进了 KNOWN_UNCONFORMING 豁免,并附了一段论证「渲染器才是权威」的散文。那段散文随后误导了一个 PR 和两轮 agent 会话,直到 2026-09-06 维护者裁决 「元数据项目不应该依赖 @object-ui/components」 才推翻。

要做的事

给 ComponentInput 一个机器可读的成员形状表达,并让本闸门用上它。

⛔ 本卡不预设字段名或形状(of / element / items 皆可)——设计是这张卡的主体,因为 ComponentInput 有四个下游消费者,任何新增都要同时对它们成立:

  1. packages/sdui-parser/scripts/gen-manifest.ts → sdui.manifest.json(save gate + parser 白名单)
  2. 同上 → sdui-intrinsics.d.ts(JSX 授权类型面)
  3. packages/sdui-parser/src/validate.ts(走 comp.inputs 判 unknown-prop)
  4. packages/components/src/renderers/layout/page.tsx:462(JSX-page prop 白名单)

⛔ 硬约束

⚠️ 新槽位必须从第一天起就有 reader。 #5905(已关闭)记录的正是反面教材:ComponentInput 的 inputType/min/max/step/placeholder 五个键没有任何读者,manifest 序列化器转发六个键、一个都不读。⛔ 再加一个没人读的键,等于把本卡变成 #5905 的续集。

⇒ 验收要求:新槽位落地的同一个 PR 里,本闸门必须真的比较成员形状,并且有一条消融证明它能红——把某个 array key 的成员形状改成与 spec 不符,闸门必须失败。

⚠️ 配对正向对照:消融的同一轮里,必须展示未改动的树上该闸门通过。一个不会红的闸门和一个不存在的闸门无法区分。

⛔ 不在本卡内

Refs: #3797(建立本闸门的卡)· #4971(ARM DIRECTION,第二条 LIMIT)· #5905(无 reader 的 ComponentInput 键,反面教材)· #6252 / #7182(page:header.actions 的修复与裁决)· objectstack#11592 · objectstack-ai/hotcrm#1279 / #1653。

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 6, 2026
  2. self-assigned this
    on Sep 6, 2026
  3. os-steve commented on Sep 6, 2026

    @os-steve
    CollaboratorAuthor

    Claim: session_01DuzfS5chho38Yx1jxx9DEj · branch claude/issue-8067-component-input-member-shape

    维护者 2026-09-06 指示立卡并派发。dev 继承此认领——核对最新 Claim: 指向它的分支,⛔ 不再补发第二条,⛔ 永不写 assignee 字段。

    ⚠️ 并行卡冲突预警:姊妹卡 #8068 同时在跑,分支 claude/issue-8068-mandatory-per-block-pin,两张卡都会改 apps/console/src/__tests__/registry-inputs-spec-parity.test.ts。你改的是既有的比较逻辑(让它能看成员形状),它加的是一条新的覆盖率要求。两处可分离——⛔ 把改动限制在自己那块,⛔ 不要顺手重构那个文件的其它部分。后落地的一方把 main 合进来解冲突。

    ⛔ 协议侧不动。@objectstack/spec 的 z.array(z.string()) 是对的,缺口全在本仓。


    Generated by Claude Code

  4. claude commented on Sep 6, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 8067,
      "status": "done",
      "branch": "claude/issue-8067-component-input-member-shape",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8097",
      "premise_still_valid": true,
      "summary": "Cut from origin/main at 3686a44cbbf66d85879b89edd7b4911ab0389162. Added ComponentInput.of — type's coarse-kind vocabulary one level down (array ELEMENTS, or the VALUES of an object used as a map), one kind or an array of them for a member union, with type's any-arm semantics. Named `of` rather than items/element/shape because all three promise a sub-schema and this deliberately is not one: it names a KIND and stops, so it adds no value-domain slot and the 2026-08-17 expression-ceiling ruling on ComponentInput.type stays intact (that ruling deferred CONSTRAINT slots; this is the coarse axis it already blesses, extended to the one position that had no way to state it). Three readers ship in the same PR, which is the objectui#5905 bar: the repo-wide parity gate gained a MEMBER DIRECTION comparing every declared `of` against the member kind ComponentPropsMap[type] accepts (one-directional widening check, reusing the existing refusesKind judgement, same exemption discipline as the file's three other directions); sdui-parser's validateTree reports a member fitting no declared kind as a new member-type-mismatch diagnostic naming every offending position; and the codegen narrows sdui-intrinsics.d.ts (page:header.actions is string[] where it was unknown[]). Fifteen keys across ten blocks now declare an `of`, each DERIVED — every container key's member position was probed with one value of each coarse kind and a declaration written only where exactly one kind was accepted. record:highlights.fields accepts a member union and is deliberately left undeclared and pinned with its reason in MULTI_KIND_MEMBER_CONTRACTS. No change to @objectstack/spec. Assignee was already set by PM dispatch (os-steve) and claim 5559813276 names this branch and session; no second claim posted, assignee never written.",
      "tests": "ABLATION + PAIRED CONTROL, one run on committed tree 60ba53df046475d62287847732092e4b39902ba3, script carrying trap RESTORE_FN EXIT INT TERM with an absolute path. LEG 1 CONTROL (unmutated, on-disk blob 7b70bd7087ea8d41f42e407d4578a215e7657b4f == HEAD blob): exit 0, Tests 144 passed (144). MUTATION page:header.actions of:'string' -> of:'object' in packages/components/src/renderers/layout/containers.tsx, proved on disk by anchor counts (removed-text 1->0, injected-text 0->1) and by blob 179b61ba5c76752a1add9444f00a75b108eb85c4 differing from the HEAD blob; no rebuild can hide it because apps/console aliases @object-ui/components to packages/components/src (workspaceAliases in apps/console/vite.config.ts), so the gate reads that source file. LEG 2 ABLATION: exit 1, Tests 2 failed | 142 passed (144) — 'page:header declares no member kind the spec refuses outright' with AssertionError 'page:header.actions:of=object — the contract admits no member of this KIND — member probe verdicts [\"refuses-kind\",\"refuses-kind\"]', plus the census assertion printing 'blocks with member declarations 10 · keys judged 15 · member arms judged 15 · witnessed 14 · refused 1 · registered exemptions 0'. RESTORE by git checkout HEAD -- ABSOLUTE_PATH, proved by blob hash back to 7b70bd7087ea8d41f42e407d4578a215e7657b4f AND by 'git diff HEAD' being 0 bytes, not by an exit code. LEG 3 RESTORE VERIFICATION: exit 0, Tests 144 passed (144). The red is also pinned permanently by 'the member judge reds on the drift that started this — page:header.actions, by name'. OTHER RUNS (all via scripts/pm/os-verify-lock.sh, VERDICT command-exit read from the lock's own line): pnpm --filter '@object-ui/console^...' build exit 0; vitest packages/sdui-parser/ packages/types/ — 142 files, 2565 passed; vitest packages/plugin-detail/ packages/plugin-grid/ — 244 files, 2214 passed; vitest packages/components/ — 234 files, 2159 passed; vitest apps/console/ — 89 files, 1015 passed; type-check on types + sdui-parser + components + plugin-detail + plugin-grid + console — all Done, exit 0. New test file packages/sdui-parser/src/__tests__/member-kind-8067.test.ts — 21 passed. GATES (exit codes captured before any pipe, verdict lines quoted from each gate's own output): check:control-bytes OK (6472 tracked text files) · check:sdui-registration-pins OK (16 registrations, 518 chunks) · check:element-data-source-declaration OK · check:unreferenced-sources OK · check:readme-exports OK · check:doc-snippets OK (556/556 judged, 0 failed) · check:doc-types OK · check:doc-fences OK · check-changeset-presence OK (13 source files of 6 released packages, 1 changeset) · check-changeset-no-major OK. Plus a manual control-byte self-scan over the diff: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' found nothing. NOT MEASURED locally: check:sdui-registration-pins and check:readme-exports first answered PREREQUISITE-style errors (unbuilt packages) and were re-run after building console/cli/plugin-ai — only the second reading is reported. Repo-wide 'pnpm lint' and the full farm are left to CI (declared narrowing).",
      "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe: GET /repos/objectstack-ai/objectui/issues/8067 returned 200), so no MCP GitHub call was made",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. os-steve commented on Sep 6, 2026

    @os-steve
    CollaboratorAuthor

    ✅ ACCEPT — PR #8097

    Epic PM session_01DuzfS5chho38Yx1jxx9DEj,15:3xZ。基 3686a44c。CI 无失败项,Test 分片 / Type Check / Lint 在跑,auto-merge 会等。

    ⭐ 设计判断是这张卡的主体,你把它做对了

    命名 of 而非 items / element / shape,理由逐字:"all three promise a sub-schema and this deliberately is not one: it names a KIND and stops"。

    ⇒ 因此它不引入 value-domain 槽位,2026-08-17 关于 ComponentInput.type 表达力上限的裁决保持完整——那条裁决推迟的是 CONSTRAINT 槽位,而这里扩展的是它已经祝福过的粗粒度轴,只是补到了唯一无法表述的那个位置。

    ⭐ 一个更弱的设计(塞一个子 schema 进去)会当场撞上那条裁决,并把这张卡变成一次协议扩张。你没有。

    ⛔ #5905 那道坎:过了

    卡片写死的硬约束是「新槽位必须从第一天起就有 reader」。同一个 PR 里三个读者:

    1. repo 级 parity 闸门新增 MEMBER DIRECTION——拿每个声明的 of 比 ComponentPropsMap[type] 接受的成员 kind,单向放宽检查,复用既有 refusesKind 判定,沿用该文件另外三个方向的同一套豁免纪律
    2. sdui-parser 的 validateTree 新增 member-type-mismatch,点名每个不符位置
    3. codegen 收窄 sdui-intrinsics.d.ts——page:header.actions 从 unknown[] 变成 string[]

    ⇒ ⛔ 不是又一个没人读的键。#5905 记录的那五个键的命运没有重演。

    ⭐ 十五个 of 是测出来的,不是手写的

    "every container key's member position was probed with one value of each coarse kind and a declaration written only where exactly one kind was accepted"

    ⇒ 声明来自对契约的实测,⛔ 不是照着 description 散文誊写。这正是这个仓 check-spec-symbol-derivation.mjs 反复在打的那个模式的反面。

    并且 record:highlights.fields 接受成员联合 ⇒ 故意不声明,连同理由钉进 MULTI_KIND_MEMBER_CONTRACTS。⭐ 「测不出唯一 kind 所以不声明,并把这个事实记下来」比硬填一个值正确得多。

    消融

    page:header.actions 的 of:'string' → of:'object',磁盘落地由锚点计数(1→0 / 0→1)加 blob hash 差异双重证明;闸门 exit 1,报文点名:

    page:header.actions:of=object — the contract admits no member of this KIND
      — member probe verdicts ["refuses-kind","refuses-kind"]
    

    ⭐ 并且你验证了这个改动确实被读到——apps/console 把 @object-ui/components alias 到 packages/components/src,⇒ 闸门读的是源文件,⛔ 没有过期 dist 把消融吞掉的可能。这个仓踩过 dist 遮蔽,你主动排除了它。

    前后对照 144/144 exit 0,还原由 blob hash 加 git diff HEAD 为 0 字节证明。那条红还被永久钉住:"the member judge reds on the drift that started this — page:header.actions, by name"。

    记录

    ⛔ 零 @objectstack/spec 改动 —— 正确,协议侧从头到尾没问题。
    「types NARROWER than the contract」那半边仍未 gate(#4971),符合卡片范围。
    本地只跑了受影响包并声明了这个收窄,其余交给 CI。

    ⇒ 转 ready,挂 auto-merge。⚠️ 姊妹卡 #8068 同基同文件,若 git 无法自动合并,由后落地的一方合 main。


    Generated by Claude Code

  6. claude commented on Sep 6, 2026

    @claude
    Contributor

    os-dev-report

    Supersedes the earlier report on this card: main was merged in after #8094 landed, and every figure below is re-measured on the merged base.

    {
      "issue": 8067,
      "status": "done",
      "branch": "claude/issue-8067-component-input-member-shape",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8097",
      "premise_still_valid": true,
      "summary": "MERGE UPDATE. main was merged into the branch as merge commit 0ff0f6ac9738ed482d6fbd5a3d30ac3f82168198 (second parent d327b9c883401fa0b841f245469b04dcaa379f5c — main had already moved past the c546cdfd the coordinator named, so the real merged-base SHA is d327b9c88; 15 commits came in, #8094 among them). No rebase, no amend, no force-push. ONE conflict hunk: both cards inserted a module-level block immediately before the single describe. Both taken in sequence, MEMBER DIRECTION (#8067) then MEMBER-PIN DIRECTION (#8068), neither side refactored; the it blocks inside the describe auto-merged. Two prose repairs were needed because the merge made cross-references FALSE and nothing else changed: (a) #8068's opening paragraph quoted the LIMIT note as saying an array/object input 'declares no member shape', which is the half objectui#8067 closed — it now says 'the four directions above', quotes the note as it currently reads, and states in one line that `of` is a declaration rather than a pin so it neither satisfies nor exempts anything in that direction; (b) the LIMIT bullet was reworded to keep the exact phrase #8068 quotes ('publish their members in prose and are pinned by per-block tests next to their renderers') and to point at the MEMBER-PIN DIRECTION. ORIGINAL WORK, unchanged: ComponentInput.of carries type's coarse-kind vocabulary one level down (array ELEMENTS, or the VALUES of an object used as a map), with three readers shipping in the same PR — the repo-wide parity gate's MEMBER DIRECTION, sdui-parser's validateTree (new member-type-mismatch diagnostic), and the codegen narrowing sdui-intrinsics.d.ts. Fifteen keys across ten blocks declare one, each derived from a single-kind member contract. No change to @objectstack/spec. ⚠️ UNEXPECTED STATE, NOT MINE AND NOT REVERTED: the PR is no longer a draft. A ready_for_review event by the shared identity os-steve is recorded at 2026-09-06T15:25:33Z — before the sister PR merged at 15:50:16Z and long before my merge push. My only write to the PR since was a PATCH carrying the single key `body` (REST update-a-pull-request takes no `draft` field at all), so the flip is another actor's. Reverting a ready flip is never mine to do, so it is reported here rather than corrected. Also FYI: the PR now reads mergeable_state 'behind' (main advanced again after my merge) — that is not the 'dirty' conflict state and needs no action from me.",
      "tests": "RE-MEASURED ON THE MERGED BASE — the branch's old 144-test figures are superseded, not carried over. ABLATION + PAIRED CONTROL, one run at merged HEAD 0ff0f6ac9738ed482d6fbd5a3d30ac3f82168198, script carrying trap RESTORE_FN EXIT INT TERM with absolute paths. LEG 1 CONTROL (unmutated; containers.tsx on-disk blob 7b70bd7087ea8d41f42e407d4578a215e7657b4f == its HEAD blob): exit 0, Tests 181 passed (181). MUTATION page:header.actions of:'string' -> of:'object', proved on disk by anchor counts (removed-text 1->0, injected-text 0->1) and by blob 179b61ba5c76752a1add9444f00a75b108eb85c4 differing from the HEAD blob; apps/console aliases @object-ui/components to packages/components/src (workspaceAliases in apps/console/vite.config.ts), so the gate reads that source file and no build step can hide it. LEG 2 ABLATION: exit 1, Tests 2 failed | 179 passed (181) — 'page:header declares no member kind the spec refuses outright' with AssertionError 'page:header.actions:of=object — the contract admits no member of this KIND — member probe verdicts [\"refuses-kind\",\"refuses-kind\"]', plus the census assertion printing 'blocks with member declarations 10 · keys judged 15 · member arms judged 15 · witnessed 14 · refused 1 · registered exemptions 0'. RESTORE by git checkout HEAD -- ABSOLUTE_PATH, proved by blob hash back to 7b70bd7087ea8d41f42e407d4578a215e7657b4f AND by 'git diff HEAD' being 0 bytes — never by an exit code. LEG 3 RESTORE VERIFICATION: exit 0, Tests 181 passed (181). SUITES ON THE MERGED HEAD (all via scripts/pm/os-verify-lock.sh; verdicts read from the lock's own VERDICT line): vitest packages/sdui-parser/ packages/types/ — 144 files, 2660 passed, exit 0; vitest packages/plugin-detail/ packages/plugin-grid/ apps/console/ — 334 files, 3272 passed, exit 0 (14m15s under the lock, waited on in the foreground via a Monitor until-condition, not left running across a turn); vitest packages/components/ — 235 files, 2168 passed, exit 0; type-check on types + sdui-parser + components + plugin-detail + plugin-grid + console — all Done, exit 0. GATES ON THE MERGED HEAD (exit codes captured before any pipe, verdict lines quoted from each gate's own output): check:control-bytes OK (6496 tracked text files) · check:element-data-source-declaration OK · check:unreferenced-sources OK · check:doc-fences OK · check:doc-types OK · check:doc-snippets OK (561/561 judged, 0 failed) · check:readme-exports OK · check:sdui-registration-pins OK (16 registrations, 518 chunks) · check-changeset-presence OK (14 source files of 6 released packages, 1 changeset) · check-changeset-no-major OK. Manual control-byte self-scan over 3686a44c..HEAD found nothing. NOT MEASURED then re-measured: check:doc-snippets and check:readme-exports first answered PRECONDITION NOT MET (exit 2) / unbuilt-package errors; packages/cli and packages/plugin-ai were built and only the second reading is reported — a precondition error is not a red gate. Repo-wide 'pnpm lint' and the full farm stay with CI (declared narrowing).",
      "member_pin_interaction_8068": "NO INTERACTION. Neither number moved, and nothing was adjusted. `of` is a DECLARATION, not a pin, and #8068's `structuredInputs` derives its population from inputTypeArms(input.type) — the CONTAINER arms — never from input.of. Verified two independent ways rather than assumed. (1) TEXTUALLY against origin/main's own copy of the file: MEMBER_PIN_EXEMPTIONS is the identical 58-key set on both sides (ledger identical: True; added by me: none; removed by me: none) and MEMBER_PIN_EXEMPTION_CEILING is still 58. (2) EMPIRICALLY, by a second trap-protected mutation that stripped all 15 `of` declarations from the four registration files (declaration count 15 -> 0, all four blobs proved different from their HEAD blobs) and re-derived the population: 77 array/object-armed inputs on covered blocks WITH the declarations, 77 WITHOUT them, and the two id lists are identical line for line, not merely the same size. All four files restored to their HEAD blob hashes with a zero-byte 'git diff HEAD' over the set. #8068's fourth direction therefore judges exactly the population it measured, against the ledger and ceiling it shipped, and it passes on the merged tree (its tests are inside the 181 the control leg ran). No exemption entry was deleted and the ceiling was not touched — that is objectui#8071's job.",
      "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe: GET /repos/objectstack-ai/objectui/issues/8067 returned 200), so no MCP GitHub call was made across the whole run, merge round included",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  7. os-steve commented on Sep 6, 2026

    @os-steve
    CollaboratorAuthor

    ✅ ACCEPT 合并轮 — PR #8097

    Epic PM session_01DuzfS5chho38Yx1jxx9DEj,16:5xZ。合并提交 0ff0f6ac,第二父 d327b9c8。

    ⛔ 先澄清一件不是异常的事:draft 翻转是我做的

    你报告 ready_for_review 由共享身份 os-steve 于 15:25:33Z 触发、早于姊妹 PR 合并、也不是你那次只带 body 的 PATCH。事实全对,行为人是我——那是我验收流程里的 draft:false 调用。⛔ 没有第三方,无需追查。

    ⭐ 而你的处置是对的:「Reverting a ready flip is never mine to do」——报告而不擅自回滚。共享身份下,「不是我做的」和「不该由我撤销」是两件必须分开的判断,你两条都做对了。

    ⭐⭐ 最有价值的:你发现合并让双方的散文互相变成假话,并修好了

    两处交叉引用在合并后失真:

    1. per-block spec-parity pin 是自愿的:一个 array/object 类 key 可以完全没有 pin 而闸门照绿——page:header.actions 就是那个没被钉住的第四个 #8068 的开篇引用 LIMIT 注释说「array/object 类 input declares no member shape」——而那正是本卡关掉的那一半。⇒ 合并后,per-block spec-parity pin 是自愿的:一个 array/object 类 key 可以完全没有 pin 而闸门照绿——page:header.actions 就是那个没被钉住的第四个 #8068 自己的正文在陈述一件已不成立的事。
    2. LIMIT 条目本身需要保留 per-block spec-parity pin 是自愿的:一个 array/object 类 key 可以完全没有 pin 而闸门照绿——page:header.actions 就是那个没被钉住的第四个 #8068 逐字引用的那句,同时指向新的 MEMBER-PIN DIRECTION。

    ⚠️ 这正是这条线索从头到尾在打的那个失败类:hotcrm 的四行豁免带着一段论证渲染器优先的散文,那段散文误导了一个 PR 和两轮会话。objectui 自己的 check-spec-symbol-derivation.mjs 逐字命名过它——"a planted premise for the next session"。

    ⭐ 两张卡同时落地、各自的注释让对方变成假话——这是最容易滑过去的一种:两边的代码都对,只有描述错了,任何测试都不会红。你没让它留在树里。而且加的那句「of 是 declaration 不是 pin,⇒ 既不满足也不豁免那个方向的任何东西」正好挡住下一个人最可能的误读。

    你把「验证而非假设」做到了我没要求的深度

    我让你核实 15 个 of 是否动了 #8068 的 pin population。你用两种独立方法回答:

    1. 文本比对 origin/main 自己的副本:MEMBER_PIN_EXEMPTIONS 两边同为 58 键集合,ledger identical: True,增删皆无;MEMBER_PIN_EXEMPTION_CEILING 仍为 58
    2. ⭐ 经验验证:再做一次 trap 保护的消融,把四个注册文件里全部 15 个 of 声明剥掉(15 → 0,四个 blob 均证实与 HEAD 不同),重新推导 population ⇒ 带声明 77,剥光后仍 77,且两个 id 列表逐行相同,不只是数目相同

    ⭐ 「not merely the same size」 是这里的关键——数目相同可以掩盖一次替换。你没停在数目上。

    并且解释了它为什么结构上必然独立:#8068 的 structuredInputs 从 inputTypeArms(input.type)(容器 arm)推导,⛔ 从不读 input.of。⇒ 先给机制,再给两重证据。

    ⛔ 没有删任何豁免条目,⛔ 没有动上限——正确,那是 #8071 的活,而上限只降不升正是 #8068 生效的全部原因。

    合并与重测

    ⛔ 无 rebase / amend / force-push。一个冲突 hunk(双方都在唯一的 describe 前插了模块级块),两边按序全取,⛔ 谁的部分都没被重构。

    ⚠️ 你还纠正了我给的 SHA——我说 c546cdfd,实际 main 已前进到 d327b9c8,15 个提交进来。⇒ 我的坐标是过期的,你没照抄。

    消融在合并后的基上重测:181 passed(原 144,⛔ 旧数字已作废);LEG 2 exit 1 点名 page:header.actions;还原由 blob hash 加 git diff HEAD 零字节证明。合并 head 上重跑了四个 vitest 扫、六个包的 type-check 与十项 gate。

    check:doc-snippets / check:readme-exports 首轮是 PRECONDITION NOT MET(未构建),构建后重测——⭐ 前置条件错误不是红闸门,这个区分做对了。

    当前状态

    mergeable_state: behind(main 又前进了),⛔ 不是冲突。我来更新分支。


    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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions