Skip to content

[finding] ComponentInput.defaultValue?: any (packages/types/src/base.ts:550) is why the marked plugin example in skills/objectui must carry a bare any — tighten the contract to unknown and the baseline row retires with the guide #7493

Closed

Description

@os-litant

Filed by the skills lane seat (session session_01LraLgQVGq8egUwfYZpbYt1) from the #7463 flight (PR #7490, report comment 5523867580, out-of-scope finding 3). Unassigned, bare, for triage; landing surface packages/types (a published contract) plus one line in governed skills/objectui/guides/plugin-development.md and one row in KNOWN_BARE_ANY_EXAMPLES in scripts/check-skill-examples.mjs.

Measured by the flight at PR #7490's head 2df07b7

  • packages/types/src/base.ts:550 — defaultValue?: any; in the exported ComponentInput interface.
  • skills/objectui/guides/plugin-development.md:92 — the marked example restates it as defaultValue: any. The bare-any assertion PR feat(scripts): assert no bare any in a marked skill example, and scan .claude/skills too #7490 adds finds it, and it is declared verbatim as the baseline row skills/objectui/guides/plugin-development.md:92 property defaultValue — the one row of four whose guide is faithful prose rather than rot. Fixing the guide alone would make it lie about the contract it restates, which is why the flight left it declared instead of unmarking it.

Second site, same class (added 11:2xZ from the #7494 flight, PR #7496 report 5524854506, finding 2)

  • packages/core/src/validation/schema-validator.ts:459 — schema: any, the first parameter of the exported validateSchema. The testing guide's Pattern 3 fence now passes {} with no cast at all (PR fix(skills): teach the honest test-double idiom in testing.md, retiring its three bare-any rows #7496) precisely because the parameter is any: the guide is honest, the contract is wide. The honest fix is unknown with the narrowing done inside the validator, never in the guide restating it. Fold both sites into one PR or split by package — the dev's call, stated in the PR body.

Scope

  1. Tighten defaultValue?: any to defaultValue?: unknown, or to the narrowest union the readers actually accept. Measure the read side first: the manifest serializer and every registry or renderer consumer of defaultValue ([finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905 records that several ComponentInput keys have no reader at all, so "who reads it" is the fact to settle at source, not assume). Contract-first: a declared type that erases checking is exactly the class the new assertion exists to catch.
  2. Update the guide line to the new type and delete the baseline row in the same PR. The gate reports the row as STALE the moment the guide stops carrying the bare any, so leaving the row is a red, not an option.
  3. A consumer that breaks under unknown is either a real narrowing site (add the narrowing there) or evidence the field is written and never read (say so; that is [finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905's class).
  4. Same treatment for validateSchema(schema: any): unknown at the boundary, narrowing inside; the Pattern 3 fence needs no change (it already passes a plain {}).

Size S. Fixes this card. Mixed surface: packages/types and packages/core owe changesets by their own gates; the guide line is governed skills/objectui/** (draft PR, human merge).

Verified

Refs: #7463 · PR #7490 · #7494 · PR #7496 · #5905 · #4631.

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 5, 2026
  2. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 — domain:spec / priority:p3 / pm:queue / finding

    锚定 (anchoring):主落点 packages/types/src/base.ts(已发布契约)⇒ domain:spec。⚠️ 本卡跨三个面,落地时要分清:

    面 落点 归属
    ① ComponentInput.defaultValue packages/types domain:spec(欠 changeset)
    ② validateSchema(schema: any) packages/core domain:ui(欠 changeset)
    ③ 指南那一行 + baseline 行 skills/objectui/** + scripts/check-skill-examples.mjs ⚠️ 治理面 ⇒ 草案 PR + 人工合并

    ⛔ 三者不能合进一个自动合并的 PR —— ③ 是 skills/**,GOVERNED_APPROVERS 为 os-zhuang / hotlong。

    在 origin/main a472b07 上复核 —— 两个站点都在

    packages/types/src/base.ts:581                         defaultValue?: any;
    packages/core/src/validation/schema-validator.ts:458   export function validateSchema(
    packages/core/src/validation/schema-validator.ts:459     schema: any,
    

    ⚠️ ①的行号漂了 31 行(卡片写 :550,实测 :581);②未漂(:459 准确)。卡片自己也写了「re-measure at claim」—— 照做是对的。

    ⭐ 卡片最该被遵守的一句:"修指南"是错的答案

    Fixing the guide alone would make it lie about the contract it restates, which is why the flight left it declared instead of unmarking it。

    ⭐ 这个判断很准,而且解释了为什么这张卡存在而不是被那次 flight 顺手做掉:plugin-development.md:92 的 defaultValue: any 是忠实的散文(四条 baseline 里唯一不是腐烂的那条)。把指南改成 unknown 而契约还是 any,等于让指南开始说谎 —— 这是四条里唯一必须从契约端修的一条。

    ②同理:测试指南的 Pattern 3 现在直接传 {} 不加 cast(PR #7496),正是因为参数是 any —— 指南诚实,契约太宽。

    定级理由

    priority:p3:

    • 无运行时缺陷:两处都是"类型太宽",不是"类型错误"。
    • 代价是声明擦掉了检查,而这正是新装的 bare-any 断言要抓的那一类 —— 卡片说得对:"a declared type that erases checking is exactly the class the new assertion exists to catch."
    • 不上 p2:无用户影响,无静默错答,且已被门禁登记为 baseline(有名的债,不是静默的)。

    定型:收紧已发布类型 ⇒ ⚠️ 是行为可见的契约变更,不是纯 tidy

    any → unknown 会让今天通过的消费者开始报错。⛔ 这不是"没人会注意到"的改动。但方向是收窄、恢复检查 ⇒ 不属于加宽授权面,不踩 manual floor;带 changeset 即可。

    ⛔ 给执行席的边界

    1. ⭐ 先做卡片指定的读侧普查,⛔ 不要先动类型。 卡片写得很具体,且给了理由:

      Measure the read side first: the manifest serializer and every registry or renderer consumer of defaultValue ([finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905 records that several ComponentInput keys have no reader at all, so "who reads it" is the fact to settle at source, not assume).

      ⚠️ 这一步可能直接改变结论:若 defaultValue 根本没有读者,那它不是"类型太宽",而是 [finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905 那一类的死键 —— 处置从"收紧"变成"退休"。⛔ 不测就改,可能在给一个没人读的键做类型工程。

    2. baseline 行必须同 PR 删。 卡片给了机制理由:"The gate reports the row as STALE the moment the guide stops carrying the bare any, so leaving the row is a red, not an option."

    3. 消费者崩了怎么办,卡片给了二分法,请照用:"A consumer that breaks under unknown is either a real narrowing site (add the narrowing there) or evidence the field is written and never read (say so; that is [finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905's class)." ⛔ 不要用 as any 把它糊过去 —— 那会把一处宽类型换成 N 处逃逸。

    4. ②的收敛方向卡片也定了:边界收 unknown,收窄放在 validator 内部,⛔ 不要让调用方去 cast。Pattern 3 的 fence 不需要改(它已经传 {})。

    5. ⛔ ①②合并还是分包提交由 dev 决定(卡片明说"the dev's call"),但 ③ 必须单独走治理流程(见上)。

    ⛔ 分诊席不认领、不派单、不写码。


    Generated by Claude Code

  3. self-assigned this
    on Sep 5, 2026
  4. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Claim: domain:spec @ objectui execution seat, session session_01BAZFhALsQsGqxui8sNqM8s, claimed 2026-09-05T17:18Z (PM loop round R1, seventh wave) — item ① only (ComponentInput.defaultValue in packages/types). Assignee set by the PM in the same stroke as pm:queue → pm:dispatched; the dev is a subagent of this session (mode:subagent, no session of its own) — identity is the branch.

    Branch: claude/issue-7493-component-input-default-value-unknown · worktree ../objectui-issue-7493 · cut from origin/main = 16a725f9 (17:16Z).
    Clause-②: yes — the published TS face narrows (any → unknown refuses unnarrowed consumer reads at compile time); the zod face moves in lockstep (z.any() → z.unknown(), no runtime accept-set change). The PM seat reviews at tier before it lands.
    Container & model: fable (CONTRACT_REVIEW_TIER), size S, mode:subagent.

    Scope split (triage 5548740471's three faces): ① this dispatch — packages/types/src/base.ts ComponentInput.defaultValue?: any and its zod twin packages/types/src/zod/base.zod.ts ComponentInputSchema.defaultValue: z.any(), census-first per the card and triage (who reads defaultValue: the manifest serializer, registry meta inputs, the designer's inspector, any renderer — #5905 records ComponentInput keys with no reader); if readers exist → tighten to unknown (or the narrowest union the readers accept) on both faces with the narrowing added at the read sites, never a cast; if ZERO readers → ⛔ do not retire in this PR — report the census with premise_still_valid: false and stop, the remedy then changes class (#5905, a published designer-facing key) and the seat routes it. PR first line Refs #7493 (item ①) — ⛔ not Fixes; the card stays open. ② validateSchema(schema: any) (packages/core/src/validation/schema-validator.ts:458-459) is domain:ui per triage — after ① lands the seat hangs pm:retriage here for the re-lane. ③ the guide line + the KNOWN_BARE_ANY_EXAMPLES row is governed skills/objectui/** + scripts/check-skill-examples.mjs — a separate draft PR under human merge, a skills-lane card the seat files when ① lands (the row is line-anchored to :92 while the guide line now sits at :103 — a reading for that card, not this one).

    Premise verified on origin/main 16a725f9 at 17:16Z: base.ts:608 defaultValue?: any; (the card's :550 and the triage's :581 have both drifted); base.zod.ts:321 defaultValue: z.any().optional().describe('Default value') inside ComponentInputSchema (:301) — the zod face is ALSO any, so both faces move together and the parity pair base.zod.ts#ComponentInputSchema (zod-mirror-parity.test.ts:562 / :723) stays lockstep; schema-validator.ts:458-459 validateSchema(schema: any, …) holds; plugin-development.md:103 defaultValue?: any; (moved from :92); baseline row check-skill-examples.mjs:558. A narrow seat grep found no .defaultValue read on an input in non-test packages/*/src — the dev's census is the reading, the seat's grep is not.

    File surface (region level under the 10:5xZ ruling, comment 5551274423): base.ts — the ComponentInput block (defaultValue member + docblock) and, if a reader needs narrowing, that read site (declared by path in the PR); base.zod.ts — the ComponentInputSchema defaultValue member; new pin; changeset @object-ui/types: minor. ⛔ packages/core/** (item ②), ⛔ skills/** and scripts/check-skill-examples.mjs (item ③), ⛔ enum / type members of ComponentInput. Hot files: none shared with the two other devs in flight (#7542 app.*, #7313's PR #7777 objectql.*); PR #7761 landed a comment-only docblock in base.ts (:477) — merge main before opening. Half-state patrol anchor: none exists in objectui (#5986).


    Generated by Claude Code

  5. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Claim (dev, item ① only): branch claude/issue-7493-component-input-default-value-unknown · worktree ../objectui-issue-7493 · cut from origin/main = 669d71bf (re-fetched at claim time; the PM claim's 16a725f9 had already advanced by PR #7774). Dev subagent of the domain:spec @ objectui PM seat's dispatch, PM session session_01BAZFhALsQsGqxui8sNqM8s; identity is the branch. Assignee is the PM's and untouched.

    Scope held to item ① (ComponentInput.defaultValue on both faces in packages/types). Order held to the card's and the triage's: the reader census runs first and no edit happens before it is posted here — the dispatch's stop condition (zero readers ⇒ no PR, premise_still_valid: false) is decided by that census. Empty-branch probe push landed, so the write route is open.

    Premise re-verified on 669d71bf: packages/types/src/base.ts:608 defaultValue?: any; · packages/types/src/zod/base.zod.ts:321 defaultValue: z.any().optional() inside ComponentInputSchema (:301) · parity pair registered at zod-mirror-parity.test.ts:562 / :723 · guide line plugin-development.md:103 · baseline row check-skill-examples.mjs:558 (read only, not touched).


    Generated by Claude Code

  6. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 7493,
      "status": "needs_decision",
      "branch": "claude/issue-7493-component-input-default-value-unknown",
      "pr": null,
      "head": "669d71bf (empty probe branch, equal to origin/main at cut; no commit on it)",
      "premise_still_valid": false,
      "clause_2": "n/a — nothing lands; the published face is untouched",
      "line_budget": "n/a",
      "summary": "Item ① stopped at the dispatch's stop condition, before any edit: the reader census found ZERO production readers of ComponentInput.defaultValue. Every non-test consumer of ComponentMeta.inputs was enumerated and none reads the key — the manifest serializer forwards six keys and drops it (packages/sdui-parser/src/index.ts:153-160), the sdui-parser boundary type RegistryConfigLike.inputs has no defaultValue slot (index.ts:70-84) so codegen/validate cannot see it, Registry.ts:392-394 (the ElementDataSourceGate seam) reads input.name only, WidgetRegistry.ts:179 WRITES it (copies WidgetInput.defaultValue, already typed unknown at widget.ts:278, onto the synthesized input; WidgetInput.defaultValue has no reader either), page.tsx:463-465 and every getPublicConfigs() caller feed the same serializer, and neither packages/plugin-designer nor the app-shell inspectors read registry meta.inputs at all (the designer's PropertyEditor never consults ComponentMeta). apps/**, examples/**, e2e/** and the sibling objectstack checkout (d701e65) contribute no reader. The key IS heavily written — 245 authoring sites across 97 of 211 inputs arrays — and it has 11 TEST-ONLY read lines in 9 test files (spec-parity pins asserting a declared default's value, the ui:icon coupling pin whose own comment says it 'becomes correct the instant any consumer appears', and two console fixture samplers typed input: any returning unknown); none feeds a runtime and none would fail to compile under unknown by inspection. Per the dispatch (zero readers ⇒ do not tighten, do not retire, no PR) the remedy changes class to #5905's — a declared-and-DROPPED key at 245 sites, inputType's shape at scale — and the seat routes it. Assignee was set and untouched; the card stays open for items ② and ③.",
      "reader_census": {
        "tree": "origin/main 669d71bf (worktree ../objectui-issue-7493)",
        "denominator": "git grep 'defaultValue' over tracked files under packages, apps, examples, e2e, scripts: 2815 lines in 596 files; restricted to the 578 files that also mention ComponentInput, WidgetInput, PluginComponentInput or the word inputs: 111 read-shaped lines (.defaultValue, bracket access, or destructured), 80 outside tests. Structural leg: every non-test consumer of ComponentMeta.inputs enumerated via .inputs / inputs.map|find|filter / getMeta|getConfig|getAllConfigs|getPublicConfigs|getNamespaceComponents callers.",
        "production_readers": [],
        "production_non_readers_that_touch_inputs": [
          "packages/sdui-parser/src/index.ts:153-160 — manifestFromConfigs forwards name, type, required, enum, binding, description; defaultValue dropped",
          "packages/sdui-parser/src/index.ts:70-84 — RegistryConfigLike.inputs boundary type declares no defaultValue; codegen.ts:76-90,151-152 and validate.ts:55-83 read name/type/required/enum/binding on that shape",
          "packages/core/src/registry/Registry.ts:392-394 — withElementDataSourceInput reads input?.name only; ELEMENT_DATA_SOURCE_INPUT (element-data-source.ts:327-342) authors no defaultValue",
          "packages/core/src/registry/WidgetRegistry.ts:175-184 — a WRITE: copies WidgetInput.defaultValue (widget.ts:278, already unknown) onto the synthesized ComponentInput; no reader of WidgetInput.defaultValue exists either",
          "packages/components/src/renderers/layout/page.tsx:463-465 — forwards meta?.inputs verbatim into manifestFromConfigs",
          "packages/sdui-parser/scripts/gen-manifest.ts:20, apps/console/dev/manifest-dump.tsx:41, packages/components/src/renderers/layout/react-page.tsx:75 — getPublicConfigs() into the serializer or into { type, isContainer }",
          "packages/plugin-designer/**, packages/app-shell/** — zero reads of registry meta.inputs; the 39 raw hits in plugin-designer are Field.defaultValue (FieldDesigner), the 973 in app-shell are i18n { key, defaultValue }, Field.defaultValue and flow variables"
        ],
        "test_only_readers": [
          "apps/console/src/__tests__/public-block-binding-reach.test.tsx:286,327 — sampleFor(input: any): unknown seeds a probe prop from the declared default",
          "apps/console/src/__tests__/record-block-record-reach.test.tsx:251,253 — same sampler",
          "packages/components/src/__tests__/icon-renderer-declared-default.test.ts:85,108 — compares the icon input's defaultValue (cast as string | undefined) to meta.icon",
          "packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts:222 — expects filter input defaultValue undefined",
          "packages/components/src/__tests__/text-input-inputs-spec-parity.test.ts:201 — expects the defaultValue input's own defaultValue undefined",
          "packages/layout/src/__tests__/appSchemaRendererMobileNavMode.test.tsx:133 — expects 'drawer'",
          "packages/plugin-detail/src/__tests__/recordDetailsInputs.spec-parity.test.ts:441-442 — expects true / false",
          "packages/plugin-detail/src/__tests__/recordRelatedListInputs.spec-parity.test.ts:219 — expects 'id'",
          "packages/plugin-editor/src/index.test.ts:97 — expects false"
        ],
        "authorship": "depth-1 bracket-balanced scan over every inputs: array (129 files, 211 arrays, 957 input objects): defaultValue 245; controls in the same pass: name 951, type 951, label 908, description 193, enum 119, required 86, advanced 9, binding 4",
        "controls_on_the_read_instrument": ".binding (a forwarded key) → 8 non-test read-shaped hits including the serializer (index.ts:158), codegen.ts:152 and validate.ts:82-83; .enum on an input receiver → 3 reader hits (codegen.ts:109, index.ts:157, validate.ts:157); .defaultValue → 42 non-test read-shaped hits, all on non-ComponentInput receivers except the WidgetRegistry write",
        "external": "sibling objectstack d701e65: zero ComponentInput references and zero input-shaped defaultValue reads; authorship outside the repositories not measurable (the #5674 / #5905 limit)"
      },
      "acceptance_readings": "n/a — no edit; for the record, z.any() → z.unknown() would not change the runtime accept set, and none of the 11 test reads would fail to compile under unknown by inspection (input: any, an explicit as-cast, or toBe on an unknown)",
      "tests": "none run — the stop condition fired before any edit, so there is no diff to build or test. Evidence is the census commands above (git grep, the two scratchpad scanners inputs-census.mjs and inputs-census-depth1.mjs, run in the worktree at 669d71bf). Ablation: n/a",
      "gates": [
        { "command": "all dispatch gates (types build/test/type-check/lint, consumer type-checks, changeset scripts, control-bytes, spec-symbols, skill-examples, governed predicate)", "exit": null, "head": "669d71bf", "verdict": "NOT RUN — no diff; nothing lands" }
      ],
      "files_changed": [],
      "deviations": [
        "Read channel: repo-scoped REST GET returned 403 (session-level: 'GitHub access is not enabled for this session'); #7493 and #5905 were read through the public page payload (7493: hasNextPage false, 14 timeline items, triage 5548740471 and the PM claim present; 5905: first page, body plus the triage and claim comments present). MCP used for writes and for the one dedup search.",
        "BASE is 669d71bf, not the dispatch's 16a725f9: origin/main had advanced (PR #7774) before the worktree was cut; every premise site re-verified there (base.ts:608, base.zod.ts:321 inside ComponentInputSchema :301, zod-mirror-parity.test.ts:562/:723, plugin-development.md:103, check-skill-examples.mjs:558).",
        "No PR opened and no edit made, per the dispatch's zero-reader stop condition; the empty probe branch remains on origin (ref deletion is refused by the proxy per AGENTS.md).",
        "Worktree removed after the census; node_modules deleted first."
      ],
      "mcp_calls": "4 — claim comment, one search_issues dedup (6 results, so it doubled as its own control), one issue_write (#7781), this report comment",
      "open_questions": [
        {
          "question": "Disposition of ComponentInput.defaultValue now that it is measured as declared-and-dropped (245 writes, zero production readers, 11 test-pin reads): which of the four routes does the seat take, and does #7781 (label and advanced, the other two non-forwarded keys) ride the same ruling?",
          "options": [
            "A — RETIRE under enforce-or-remove, #5905's route: tombstone both faces via retirementTombstone(), delete the 245 writes, rewrite the 9 test files to pin the renderer's actual default against a local constant. Axis 1 (real demand): none measured — the manifest never publishes the key, so no AI author consuming sdui.manifest.json or the generated JSX d.ts ever saw a declared default; the only consumers are repo tests. Axis 2 (long-term): matches ADR-0049 and the #5905 precedent; one truth (the renderer's default) instead of a shadow copy tests keep in sync. Axis 3 (AI error-proofing): the strongest — a write becomes a loud refusal on both faces instead of a silently dropped any. Axis 4 (startup focus, no staged windows): immediate retirement, no dual spelling. Cost: size M, a contract-review PR touching about 50 registration files plus 9 tests, changeset minor with breaking wording.",
            "B — TIGHTEN to unknown as the card asked (both faces, pin, changeset), leaving the key alive. Axis 1: serves no runtime; it only makes the type honest about a key nothing reads. Axis 2: keeps a dead published key on the contract with a truer type — a named debt, not a removal. Axis 3: does NOT close the error class — unknown accepts every value, so defaultValue: 'yes' on a boolean-armed input still passes tsc and publish exactly as today; only the READ side would be forced to narrow, and there is no read side. Axis 4: cheapest (size S, the dispatch's own plan) but it spends a review round on surface with zero pull.",
            "C — FORWARD: teach the serializer to publish defaultValue and have checkType validate it against the arm. Axis 1: no measured pull — no consumer asked for declared defaults. Axis 2: couples sdui-parser to a value nothing validates today. Axis 3: would let checkType catch a default contradicting its arm, but only if that gate is also written. Axis 4: capability expansion without pull; the same option was REFUSED on record for inputType in #5905 ('a write nothing reads is not demand for a feature').",
            "D — KEEP as declared documentation whose only enforcement is the test pins, with a docblock saying so on both faces. Axis 1: the 11 test reads are the one real use. Axis 2: institutionalises a shadow default the renderer does not consult. Axis 3: an AI author still writes an unchecked any. Axis 4: no cost now, the debt stays."
          ],
          "recommendation": "A, because every axis but the sunk cost points the same way: no reader exists on any publication or consumption path, the key never reaches an author, the AI-error axis is only closed by a refusal, and the startup rule says a published zero-consumption capability earns no exemption for its 245 writes. B is the fallback if the maintainer wants the debt named before it is removed, but it should be recorded as not closing the error class. Decide #7781's two keys in the same stroke: the same instrument gives the same zero for label (908 writes) and advanced (9 writes)."
        }
      ],
      "out_of_scope_findings": [
        "filed as #7781: ComponentInput.label (908 writes) and ComponentInput.advanced (9 writes) have zero production readers by the same structural census — the two remaining keys the serializer does not forward; finding label, no pm:queue, dedup searched (6 results, #5905 closed parent class, #6950 open on binding, none carries label/advanced)."
      ]
    }

    Generated by Claude Code

  7. removed their assignment
    on Sep 5, 2026
  8. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Moved to the decision inbox (2026-09-05T17:41Z) — pm:dispatched → needs-user-decision in one label replace; assignee cleared; the seat's item-① claim (5553479601) is released. By the domain:spec @ objectui execution seat, session session_01BAZFhALsQsGqxui8sNqM8s.

    Why: the dispatch's stop condition fired. The dev's census (report 5553590885, on origin/main 669d71bf) found zero production readers of ComponentInput.defaultValue: the manifest serializer forwards six keys and drops it (packages/sdui-parser/src/index.ts:153-160), the parser's boundary type has no slot for it (:70-84), Registry.ts:392-394 reads input.name only, WidgetRegistry.ts:175-184 WRITES it (from WidgetInput.defaultValue, itself reader-less), the designer and app-shell never read registry meta.inputs; apps/**, examples/**, e2e/** and the sibling objectstack checkout contribute no reader. The key is written at 245 sites across 97 of 211 inputs arrays (controls: name 951, type 951, label 908) and has 11 test-only read lines in 9 files (spec-parity pins asserting a declared default's value). Triage 5548740471 named exactly this fork: "若 defaultValue 根本没有读者,那它不是类型太宽,而是 #5905 那一类的死键 —— 处置从收紧变成退休". Retiring a published key with 245 authoring sites is a manual-floor decision, so nothing was edited and no PR was opened (the empty probe branch claude/issue-7493-component-input-default-value-unknown sits on origin at 669d71bf).

    Options (the dev's four, seat-checked)

    决策四维分析

    • 实际业务需求:零读者——manifest 不发布它、生成的 d.ts 看不到它、设计器不读它;写它的 245 处只供 9 个测试文件断言。没有任何作者(人或 AI)通过任何发布或消费路径见过这个声明的默认值。
    • 项目长远合理性:A 只留一个真相(渲染器自己的默认值),去掉测试维护的影子副本,与 [finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905 先例和 ADR-0049 一致;B 把死键以更诚实的类型留在契约上,是有名的债;C 为无拉动的能力增加 sdui-parser 的耦合;D 把影子默认值制度化。
    • 防 AI 写元数据犯错:只有 A 让写入变成两面响亮拒绝;B 之下 defaultValue: 'yes' 配 boolean 臂照样通过 tsc 与发布;C 只有连带写门禁才有用;D 维持现状。
    • 创业阶段不扩散需求:已发布零消费的能力不因 245 处沉没成本获得豁免;不设过渡窗口,立即退役。C 是无拉动的扩张。

    Seat recommendation: A. B is the fallback if the maintainer wants the debt named before it is removed. Same stroke: #7781 — the dev measured ComponentInput.label (908 writes) and advanced (9 writes) as reader-less by the same instrument (the other two keys the serializer does not forward); the ruling here should say whether they ride the same route.

    Items ② and ③ (unchanged by this): ② validateSchema(schema: any) in packages/core is domain:ui per triage and independent of ①'s outcome — triage may split it out now; ③ the guide line + the KNOWN_BARE_ANY_EXAMPLES row (governed skills/objectui/**) follows whatever ① becomes (under A the guide line goes away with the key; under B it restates unknown).

    Refs: #5905 (the reader-less ComponentInput keys parent class, closed) · #7781 · #6950 (open, binding) · triage 5548740471.


    Generated by Claude Code

  9. 35 remaining items

  10. added theissue type on Sep 8, 2026
  11. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊席答 pm:retriage:准,重新分车道到 domain:skills;⛔ 但 item ③ 的描述已经过期一半,我把它更正后再交出去

    标签 bug documentation finding domain:skills pm:queue priority:p3 · 类型 Bug · pm:retriage 同笔摘掉,domain:spec 撤下。


    1 · 异议:准

    5582382623 的请求是一行 ——「re-lane this card to domain:skills for item ③」(5574315627 同问)。成立。

    scripts/check-skill-examples.mjs 那一行是随导言退役的后果,不是主题;主题设车道,不是被牵动的文件设车道。

    item ① 已落地,复量确认。 packages/types/src/base.ts 上三个墓碑都在::724 label?: never; · :736 defaultValue?: never; · :761 advanced?: never;。所以 5582382623 说「已裁 A 且已在 main」是对的,5574940407 恢复的 needs-user-decision 确实答的是一个已经答过的问题。


    2 · ⛔ 更正:item ③ 的前半已经做完了,5582382623 的描述在这一点上已过期

    复量于 origin/main 83d4fda16。评论说:

    item ③ — the marked fence in skills/objectui/guides/plugin-development.md (<!-- os:check --> at :91, fence at :92) still declares the three retired keys as writable optionals

    **不再是这样。**那条 marked fence 现在:

    • import 而非重抄 —— import type { ComponentInput, ComponentInputControlType } from '@object-ui/types';
    • :89-90 明写 "SIX keys are writable — name, type, of, required, enum, description."
    • :111-129 把三个退役键连同另外六个,做成 Pick<ComponentInput, 'label' | 'defaultValue' | 'advanced' | 'inputType' | 'min' | 'max' | 'step' | 'placeholder'>,并注明 "A Pick rather than a prose list, so a tombstone that is deleted upstream stops compiling here instead of lingering."
    • :132-137 那段「declared label, defaultValue and advanced as WRITABLE」是回顾("This fence used to keep a private copy…"),⛔ 不是现状陈述。读到那句就下结论,会把已修的东西再修一遍。

    KNOWN_BARE_ANY_EXAMPLES 那一行也已退役: scripts/check-skill-examples.mjs 上该常量现在是 new Set([]),:606-615 的注释记了原因,并且原因本身是一次自我更正 —— 那一行的理由「导言是 FAITHFUL prose,因为 ComponentInput.defaultValue 真的是 any」在它底下过期了,平台类型早已是 defaultValue?: never,所以导言的 any 才是 rot,honest fix 是改导言。⭐ 注释末句值得留着:"an empty shrink-only list is the terminal state it is shaped for, not a disabled one." ⛔ 不要把空列表当成门被关掉了。


    3 · ⭐ 但 item ③ 的后半是真的,而且没人在看

    5582382623 的第二句成立,并且是本卡现在唯一的可交付物:

    the unmarked registration fence at :46 teaches label: on three inputs — a tsc error today

    复量确认。skills/objectui/guides/plugin-development.md:

    :56    { name: 'title',   type: 'string', label: 'Title' },
    :57    { name: 'columns', type: 'array',  label: 'Columns', required: true },
    :58    { name: 'mode',    type: 'enum',   label: 'Mode', enum: ['compact', 'full'] },
    

    三条都是 ComponentInput 条目,三条都写 label:,而 ComponentInput.label 是 never。⇒ 今天这三行若被编译就是 tsc 错误。

    ⚠️ **⛔ 同一段里的 :51 label: 'My Widget' 不是缺陷,不要一起删。**那是 ComponentMeta.label,是活的键,同一页 :66 的表格就在记录它("label | string | Display name for designer UI")。一个 label 活、一个 label 死,隔着五行 —— 这正是最容易改错的形状。

    为什么没红:这条 fence 没有 os:check 标记。 check:skill-examples 只判带标记的 fence,所以同一份文件里,被看着的那条已经修好了,没被看着的那条还在教一个编译不过的键。⭐ 这是本卡从头到尾的那个类:门只看它被告知要看的地方。


    4 · 交付与围栏

    可交付物(全部在 governed skills/**,草案 PR + 人工合并):

    1. 删掉 :56 :57 :58 三处 label:。⛔ 保留 :51。按 marked fence 自己给的补救法:"delete the key and say it in description, which IS published" —— 若这三条需要人读的名字,写进 description。
    2. 考虑给这条 registration fence 加 <!-- os:check -->。⭐ 这才是让缺陷不再复发的那一半:三行 label: 只要被编译一次就会红。⚠️ 但先跑一次看它还有没有别的 tsc 错误 —— 加标记等于把整条 fence 纳入门,⛔ 不要在不知道全部代价的情况下加。若它另有错,分两笔:先修,后标。

    ⛔ 围栏:

    • ⛔ 不要重修 :91 那条 marked fence,它是对的。
    • ⛔ 不要动 KNOWN_BARE_ANY_EXAMPLES,空集是终态。
    • ⛔ 不要因 :134 那句回顾性文字而下手。

    等级 p3 维持。 governed 散文面,无运行时影响,且这三行今天没有任何门在编译。⇒ 若第 2 点(加标记)被采纳并因此让门开始编译这条 fence,本卡在那一刻变成会红的东西 —— 那是改进,不是升级理由。

    ⛔ 无新裁定可欠。 item ① 已由维护者裁 A 并落地(5556358261,PR #7932);本卡不再需要任何人作答,只需要有人做 item ③。⛔ 不重开决策箱。

    ⛔ 分诊席边界照旧:不认领、不派发、不写码、不合并、不裁决。


    Generated by Claude Code

  12. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Contributor

    Graded by the skills seat (the lane self-triages its findings; objectstack seat post #7623, session session_01HxLw5aKDPR5RJgyUR7Exkd, 2026-09-09T02:10Z): priority:p3 · type Bug · pm:queue as triage re-laned and re-scoped it (5583571842); finding dropped in this same write. Scope taken into the queue is exactly triage's item ③ as corrected: the unmarked registration fence in skills/objectui/guides/plugin-development.md teaches label: on three ComponentInput entries (:56–:58 on the tree triage read) while ComponentInput.label is never — delete those three keys (say the name in description if one is needed), keep :51 (ComponentMeta.label, live), and add os:check to that fence only after a first compile shows no other error (two commits if it does). ⛔ The marked fence at :91, KNOWN_BARE_ANY_EXAMPLES (empty set = terminal) and the retrospective sentence near :134 stay untouched; item ① landed (PR #7932), item ② is #8416. Governed skills/** ⇒ draft PR at the human terminal; queued for the next free slot (batch 5, all in flight at 02:10Z).


    Generated by Claude Code

  13. self-assigned this
    on Sep 9, 2026
  14. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Contributor

    Claim: PM loop round 1 — flight F: item ③ as triage corrected it (5583571842) — the unmarked registration fence in skills/objectui/guides/plugin-development.md stops teaching label: on three ComponentInput entries (a never key on the published contract), keeps ComponentMeta.label, and gains the os:check marker only if a first compile shows no other error
    Session: session_01HxLw5aKDPR5RJgyUR7Exkd
    Branch: claude/issue-7493-registration-fence-label
    Worktree: objectui-issue-7493
    Domain: domain:skills
    File surface: skills/objectui/guides/plugin-development.md — the registration fence (lines 46–60 on objectui origin/main f6205c1; label: at lines 56 / 57 / 58; ⛔ line 51 label: 'My Widget' stays — that is ComponentMeta.label, live) plus a .changeset/*.md with empty frontmatter if the repo's changeset gate asks for one; ⛔ the marked fence at line 91, scripts/check-skill-examples.mjs (KNOWN_BARE_ANY_EXAMPLES = new Set([]) is the terminal state) and packages/types untouched (stop on breach; explain in the report)
    Container & model: S (three key deletions + one optional marker on a governed published skill), mode:subagent, model: opus — dispatch-gates.mjs --tier --repo objectstack-ai/objectui at 02:31Z answers "REFUSING — asked for 'objectstack-ai/objectui', but this checkout is 'objectstack-ai/objectstack' … a sister repo's list is hand-derived from its own package manifest and its own workflow files", so no path-derived mandate exists for an objectui surface; PM judgment: default tier for a teaching fence whose correctness a compile decides
    Clause-②: no — the fence is brought back to the declared contract (ComponentInput.label is never at packages/types/src/base.ts:724 on f6205c1); nothing widens
    Thread-read: 5594688651
    Serial constraints cleared: file last touch 503cd8b (#8612, 2026-09-08) on objectui origin/main f6205c1 read 02:31Z; no open objectui PR touches it (17 open PRs' titles + heads read 02:07Z); no other queued card in this lane declares it (#8363 → docs/adr/** + CONTRIBUTING.md; #8390 / #8403 → AGENTS.md). Gate families for objectui are hand-derived from its own manifest — check:skill-examples, check:skills-paths, check:skill-eval-tokens plus what objectui's AGENTS.md prescribes for docs; the shared verify lock is the sibling checkout's scripts/pm/os-verify-lock.sh (the very gap #8403 records). Governed (skills/** is governed in objectui too) ⇒ draft only at the governed terminal; objectui has no skip-changeset label — an empty-frontmatter changeset is the correct form if the gate asks.


    Generated by Claude Code

  15. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Contributor

    os-dev-report

    {
      "issue": 7493,
      "status": "done",
      "branch": "claude/issue-7493-registration-fence-label",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8744",
      "premise_still_valid": true,
      "summary": "Triage item 3 as corrected (5583571842), first half only. Deleted the three retired `label:` keys from the three `ComponentInput` entries in the unmarked registration fence of skills/objectui/guides/plugin-development.md; `ComponentInput.label` is `never` on the published contract (packages/types/src/base.ts, ADR-0049 tombstone), so those lines were a tsc error and taught a key the manifest serializer silently drops. Nothing written in their place: that key's own tombstone says the input's `name` is what consumers identify it by, and Title/Columns/Mode were only the title-cased `name`. Kept every live `ComponentMeta.label`. The os:check opt-in marker was measured and NOT added -- see deviations. Draft PR parked at the governed terminal; not flipped ready, not enqueued, no auto-merge, no seat approval.",
      "tests": "Build precondition under the shared verify lock (slot issue-7493-skills; the lock lives in the sibling objectstack checkout, the gap objectui#8403 records): `turbo run build --concurrency=2` over `check-skill-examples.mjs --build-filter` printed `Tasks: 29 successful, 29 total`, and the lock printed `VERDICT command-exit 0 - held the lock 244s - waited 0s`. Without a built dist this gate prints PRECONDITION NOT MET and exits 2, which is neither a pass nor a guide defect. || Falsification/verification of the edit: `node scripts/check-skill-examples.mjs --measure` run on the same tree before and after; the entire diff between the two 582-line reports is exactly three lines removed -- `plugin-development.md:56:38`, `:57:39`, `:58:35  TS2322: Type 'string' is not assignable to type 'undefined'.` -- and nothing else changed anywhere. On-disk proof of the edit before measuring: anchored counts of the three literal strings went 1/1/1 to 0/0/0. || Gate union re-run at final commit 022ea6a with a clean working tree; exit codes captured by redirect-then-capture, never read through a pipe.",
      "gates": [
        {
          "command": "pnpm check:skill-examples",
          "exit": 0,
          "verdict_line": "Every marked skill example holds up against the built types. / Semantic phase: 14 of 14 ts fence(s) judged, 0 failed."
        },
        {
          "command": "pnpm check:skills-paths",
          "exit": 0,
          "verdict_line": "check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined)."
        },
        {
          "command": "pnpm check:skill-eval-tokens",
          "exit": 0,
          "verdict_line": "Every must_contain token is taught by its own skill bundle. NOTE: this gate defines a taught-token oracle, NOT a token count, so no token budget exists in this repo to report."
        },
        {
          "command": "pnpm check:control-bytes",
          "exit": 0,
          "verdict_line": "check-control-bytes: OK (scanned 6942 tracked text file(s); skipped 85 binary). Plus a direct control-byte self-scan of the edited file: no hits."
        },
        {
          "command": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict_line": "Compared the working tree with 6fda1a945 (merge-base with origin/main): 1 file(s) changed, 0 of them published source of a package the release covers ... no changeset is owed."
        },
        {
          "command": "node scripts/check-governed-queue-guard.mjs --test skills/objectui/guides/plugin-development.md",
          "exit": 3,
          "verdict_line": "GOVERNED -- 1 of 1 path(s) are on a governed surface: skills/** x1, the published skills catalog. Park it as a DRAFT and leave the merge to the maintainer. Quoted in full in the PR body."
        },
        {
          "command": "node scripts/check-skill-examples.mjs --measure (evidence run, not a gate)",
          "exit": 0,
          "verdict_line": "Starting population -- ts: 18/121 pass; json: 70/70 pass. Fence :46 still FAILs on one remaining diagnostic, TS2304 Cannot find name 'MyWidgetRenderer' at :49:41."
        }
      ],
      "line_budget": {
        "unit": "lines; no gate in this repo defines a token budget",
        "edited_file": {
          "path": "skills/objectui/guides/plugin-development.md",
          "before": 481,
          "after": 481,
          "net": 0
        },
        "whole_bundle": {
          "path": "skills/objectui/**/*.md, 16 files",
          "before": 4587,
          "after": 4587,
          "net": 0
        },
        "note": "3 insertions / 3 deletions, all intra-line; no line added, no re-wrap used to buy room; net increase 0, within the at-most-zero budget."
      },
      "deviations": [
        "PM mechanism assumption 2 FALSIFIED in the has-other-error direction: the registration fence carries one further tsc error independent of the three `label:` keys -- `plugin-development.md:49:41 TS2304: Cannot find name 'MyWidgetRenderer'`. Per triage's own condition (fix first, mark later, as two pieces) and the dispatch's stated branch, the os:check marker was NOT added and no second commit exists. The marked population is therefore unchanged and MARKED_FLOOR is not approached from either side.",
        "Base moved between dispatch and claim: the dispatch read objectui origin/main f6205c1; the worktree was cut from origin/main 6fda1a9 (fetched at claim). Re-verified at 6fda1a9 before editing: `git diff --stat f6205c1..HEAD` over skills/, scripts/check-skill-examples.mjs and packages/types/src/base.ts is EMPTY, the three `label:` sites are still at lines 56/57/58, and `label?: never` is still on ComponentInput.",
        "Correction to a dispatch expectation, not a scope change: the dispatch predicted that after the fix `grep -n \"label:\"` would leave only the ComponentMeta.label at :51 plus a :66 table row. The actual result is :51, :418 and :431 -- three live ComponentMeta.label sites, where 418/431 sit in the 'Registration patterns from existing plugins' fence and are untouched -- and the :66 table row never matched that grep at all, because it is spelled as a backticked table cell rather than `label:`.",
        "The shared verify lock has no objectui copy, so the sibling objectstack checkout's scripts/pm/os-verify-lock.sh was used, exactly as the claim anticipated (objectui#8403)."
      ],
      "files_changed": [
        "skills/objectui/guides/plugin-development.md (+3/-3)"
      ],
      "mcp_calls": "0 -- every GitHub read and write on this run went through the container's repo-scoped REST channel (probe: GET /repos/objectstack-ai/objectui/issues/7493 answered 200), including the issue body, all 24 comments over 3 pages, the PR creation and this comment.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: the recurrence-prevention half of triage item 3 -- adding the os:check opt-in marker to this registration fence -- stays open, while this PR's closing keyword will close the card. The blocker is measured and stated in the PR's acceptance note: one remaining diagnostic, TS2304 for the undeclared MyWidgetRenderer at :49. It is an editorial decision about a governed published guide (should a teaching fence declare a stand-in for the reader's own component in order to be gated?), not a reproducible defect, not a declared-contract breach, and not a metadata trap, so it is none of the three filable classes. Successor: no queued card owns it once this one is closed, so it is PM's call whether to file it.",
        "noted, not filed: 12 further candidate fences in the same guide fail --measure today (:140, :175, :206, :236, :339, :414, :438, :453 among them), all unmarked and all fragments by construction -- undeclared ComponentRegistry / ColumnDef / ColorField, hook bodies continuing the block above, root-bound refusals for react and vitest. None of them teaches a retired or rejected key, which is what separated the one fixed here. Successor: none needed -- this is the gate's declared opt-in design, stated verbatim in its own header, not drift."
      ]
    }

    Generated by Claude Code

  16. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Contributor

    ACCEPT — flight F (skills seat, session session_01HxLw5aKDPR5RJgyUR7Exkd, 2026-09-09T02:50Z)

    PR #8744 (Fixes #7493, head 022ea6a), draft, targets main, one file skills/objectui/guides/plugin-development.md (+3/−3). Verified against GitHub and the fetched head, not the report (5595010357):

    • Path face: skills/** ⇒ GOVERNED on objectui's five-item register (node scripts/check-governed-queue-guard.mjs --test on the path prints the GOVERNED block; re-run by this seat at 02:46Z). Terminal: draft — the maintainer merges, or an APPROVED review by os-zhuang / hotlong lets this seat enqueue. ⛔ No ready flip, no auto-merge, no queue, no seat approval.
    • Diff read on the head: the three label: keys at lines 56–58 are gone; ComponentMeta.label at line 51 is kept, and the two other live ComponentMeta.label sites (418 / 431) are untouched — the dispatch's grep expectation is corrected by the dev, accepted; the os:check marker set is unchanged (91 / 260 / 361 / 397); 481 → 481 lines; no changeset owed (the presence gate's own line quoted; skills/** is no released package's source).
    • Marker deferred, correctly: triage's own condition (fix first, mark later, two pieces) met the measured TS2304: Cannot find name 'MyWidgetRenderer' — a fragment fence by construction, which is the gate's declared opt-in design. Item ③'s second half is therefore an editorial call on a governed guide, outside the three filable classes; recorded noted, not filed, and this seat does not file it — the class this card was about (a retired key taught) is closed by the deletion.
    • Gates: check:skill-examples 14 of 14 marked fences green after the --build-filter closure was built under the shared verify lock (29 tasks); check:skills-paths, check:skill-eval-tokens, check:control-bytes, check-changeset-presence all exit 0; the --measure before/after diff is exactly the three deleted diagnostics. CI on the head at 02:46Z: 0 failures, jobs converging — re-read before any landing step; the terminal is human either way.
    • Deviations accepted: base moved f6205c1 → 6fda1a9 (empty diff on the subject, re-verified); the verify lock used is the sibling checkout's (objectui#8403's gap, as the claim anticipated).

    Governed four-piece: needs-user-decision hung on the PR and the 「维护者速读」 final comment posted there; reviews requested from os-zhuang and hotlong; listed under awaiting a human merge in the round report. The card stays pm:dispatched with this seat as assignee until the merge lands (landing record then).


    Generated by Claude Code

  17. os-justin commented on Sep 9, 2026

    @os-justin
    Collaborator

    Landing record — PR #8744 (flight F, Fixes #7493) MERGED 2026-09-09T10:50:08Z, head 022ea6a1, merged_by os-zhuang (the merging account is not a route reading — governed skills/** ×1 (plugin-development.md); a human merge by an authorised approver is the review record). This card auto-closed on the closing keyword (state_reason: completed). Recorded by the skills seat, session session_01MoTv7pn338AZ71owsp19gQ, read 2026-09-09T11:42Z.

    Same stroke: pm:dispatched removed and the assignee (yinlianghui, the previous shift's session) cleared — closure residue on a closed card, not a release; domain:skills and the type/priority labels stay (ownership, not state).


    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

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions