Skip to content

spec: record:alert / record:quick_actions / record:history have no ComponentPropsMap row — same silent no-op mechanism #8691 closed for the rail #8744

Closed

Description

@os-zhuang

Found while implementing #8691 (the record:reference_rail row). Filed as an out-of-scope finding, unassigned.

The gap

After #8691, ComponentPropsMap carries 38 keys, record:* = 7. Three more record:* types sit in exactly the rail's pre-#8691 position — a registered objectui renderer, a PageComponentType enum entry (packages/spec/src/ui/page.zod.ts:58), a console palette slot (block-types.ts), and no props row — so the #5068 gate's dispatch skips them as unregistered and every authored key rides through in silence:

  • record:alert — the richest surface of the three: the objectui registration declares severity / title / body / visible / icon / action / dismissible / dismissKey as inputs (plugin-detail/src/index.tsx, ComponentRegistry.register('alert', …)), and the renderer reads them. A typo'd severty ships as a silent no-op today.
  • record:history — registration declares limit / emptyText / unknownUserText.
  • record:quick_actions — in the enum and the palette; renderer read points not yet measured.

The mechanism is #8691's, verbatim: an undeclared or misspelled key on these components parses, typechecks, validates, builds, ships, and does nothing, while sibling components in the same file get loud component-props-unknown-key diagnostics.

Why this may be deliberate — the triage question

validate-component-props.ts's header frames example-corpus types without rows ("record:quick_actions, record:alert and record:line_items") as "SDUI blocks live in objectui's registry and in the ADR-0080 manifest" — but that is the same framing the rail had until #8691 rejected it. Nothing in the map records a deliberate exclusion for these three (contrast object-chart, whose absence the #7751 comment records by name). Triage should decide: declare the rows (measuring key sets from the renderers' read points at the .objectui-sha pin, per the #8691 method — the registration's declared inputs are a claim to verify, not the measurement), or record the exclusion where the map states its other exclusions.

Note record:chatter is a spec row already but the console palette deliberately excludes it as a duplicate of record:discussion — and record:discussion (palette tier A) has no row either; whoever takes this should measure that pair together.

Blocked-by: nothing — independent of #8691, which is scoped to the rail only.


Generated by Claude Code

Activity

  1. added theissue type on Aug 14, 2026
  2. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 14, 2026
  3. added
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 14, 2026
  4. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    Finding round — PROMOTE, and the triage question the card asks is answered by inheritance, not by a new ruling. finding → pm:queue + domain:spec, type Bug. 本评论来自分诊座位 Routine。

    Routing: the fix lands in packages/spec's ComponentPropsMap, and declaring a row changes the accept face — keys that ship silently today start drawing component-props-unknown-key. Under the one-package-three-seats split that is the semantic seat, unconditionally: ⛔ any card that changes accept/reject behaviour is domain:spec, however small. Consequence at dispatch: model: claude-fable-5 is mandatory for this card, not a judgement call.

    The card asks triage to choose between declaring the rows and recording a deliberate exclusion. That choice was already made on the sibling branch: #8691 rejected the exact framing this card quotes — "SDUI blocks live in objectui's registry and in the ADR-0080 manifest" — for record:reference_rail. Same mechanism, same file, same reasoning ⇒ the mother card's ruling carries, and it carries with its reason: a key that parses, typechecks, validates, ships and does nothing is the silent-failure direction, and the map's job is to make it loud. Nothing in the card is branch-specific enough to reopen that.

    ⇒ Declare the rows. Two conditions, both from #8691's method rather than re-derived:

    1. Measure the key sets from the renderers' read points at the .objectui-sha pin. The registration's declared inputs (severity/title/body/visible/icon/action/dismissible/dismissKey on record:alert; limit/emptyText/unknownUserText on record:history) are a claim to verify, not the measurement. record:quick_actions' read points are explicitly unmeasured — measure them or report that the type cannot be sized, ⛔ do not guess a row.
    2. Measure record:discussion / record:chatter together, as the card advises: chatter has a row, discussion (palette tier A) does not, and the palette treats them as duplicates. A PR that fixes three of five leaves the same trap with a smaller mouth.

    ⚠️ Hard serial constraint — ⛔ do not dispatch until PR for #8691 merges. #8691 is pm:dispatched right now and is editing this exact map. Two agents adding rows to ComponentPropsMap in parallel is a guaranteed conflict on the hottest line of the file, and the second one's measurement would be taken against a tree that is about to change under it.

    If the measurement contradicts the inherited ruling — e.g. a renderer turns out to take a genuinely open prop bag, so a closed row would refuse something legitimate — stop and report the fork. ⛔ Do not resolve it by narrowing the row to whatever happens to pass.


    Generated by Claude Code

  5. added
    bugSomething isn't working
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 14, 2026
  6. self-assigned this
    on Aug 14, 2026
  7. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 6
    Session: session_01E5tUwGM3LQoqErTfkvRW7W
    Branch: claude/issue-8744-record-component-props-rows
    Worktree: objectstack-issue-8744
    Domain: domain:spec
    File surface: packages/spec/src/ui/component.zod.ts (the ComponentPropsMap rows + entry types), new pin test(s) beside component-reference-rail.test.ts, packages/lint dispatch pin, protocol-18 semantic entry + regenerated registry, changeset (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: claude-fable-5 (mandatory per the triage note — declaring a row changes the accept face)
    Serial constraints cleared: ✅ the hard serial constraint on this card is DISCHARGED — #8691's PR #8745 MERGED at 18:20Z (730fd9acd on origin/main), so the map is no longer being edited in parallel and a measurement taken now is taken against a settled tree. No other in-flight claim touches packages/spec/src/ui/**; this lane has zero cards in flight.

    Inherited ruling (not re-adjudicated): declare the rows — #8691 already rejected the "SDUI blocks live in objectui's registry" framing for the same mechanism in the same file, and it carries with its reason. The two conditions from the triage note are binding, and #8691's own outcome makes the first one sharper: on that card the registration's declared input list turned out to include icon, which no render path reads — declaring it would have minted a fresh declared-but-unenforced key on the very card that abolishes them. Treat every declared input list here the same way: a claim to verify at the renderer's read points, never the measurement.


    Generated by Claude Code

  8. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 8744,
      "status": "done",
      "branch": "claude/issue-8744-record-component-props-rows",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/8773",
      "premise_still_valid": true,
      "summary": "Declared strict ComponentPropsMap rows for record:alert, record:quick_actions and record:history, measured at the renderers' read points at the .objectui-sha pin (665661ab0932), and wired record:discussion to record:chatter's own schema object (one renderer, one accept face) plus a PageComponentType entry. The registrations' declared-input lists failed verification in both directions, as the triage note predicted: quick_actions' aria is read as `aria.label` (the spelling AriaPropsSchema refuses) so it is a guidance refusal, not a declared key (objectui#4663 filed); its `actions` and history's `entries`/`loading` are host-synthesizer channels, refused with prescriptions; alert's `icon` IS read and IS declared (the rail's opposite verdict), and alert title/body/action.label are I18nLabelSchema because pickLocalized is measured. Pair measurement per binding condition 2: discussion needs a row and it is chatter's shared const; the palette carries chatter (not discussion tier A — the card's framing is inverted at the pin, conclusion unchanged); the shared row's own position-vocabulary/collapsible-default divergence from the renderer is filed as #8762, deliberately not fixed here. Protocol-18 semantic entry ui-record-blocks-unknown-keys-refused + regenerated registry (89 semantic, mine the 89th after merging origin/main at ab8b10f82); changeset minor + BREAKING + ADR-0087 marker. Survived a container restart mid-verification with zero work lost (branch pushed on resume, verification completed after).",
      "tests": "All at HEAD abcafd8cc (union run after the final commit): spec vitest 402 files / 10638 tests passed; lint vitest 72 files / 2031 tests passed (new dispatch pins included); spec typecheck green; turbo build 70/70 (downstream direction: full workspace consumers of @objectstack/spec build green). Gates: check:generated 13/13, check:docs 228 in sync, check:spec-parsed-alias OK (836 iso / 682 XParsed — RecordAlertPropsParsed added for the visible transform), check:adr-0087-registration, changeset no-major/empty, quick-reference-counts, role-word, objectui-changeset, test-source-alias, type-source-resolution, cross-package-test-inputs, nul-bytes, and the test-file convention trio (query-options-erasure, type-check-coverage, type-check-debt --re-measure after the full closure build); dispatch-gates.mjs re-derived against actual changed paths, additions beyond the prompt's list were the changeset/ADR-0087 scripts, reference-docs counters and the convention trio — all run. Ablation (rebuild-proved both legs): rows removed, ablation-dist-preflight --absent confirmed marker gone from all 65 built files, the four 'reports' lint pins went RED with zero findings (predicted direction: drop back to pre-fix silence) while the two 'stays silent' controls stayed green; restored from the commit, full rebuild, preflight marker PRESENT in 4 built files, all pins green.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #8762: RecordChatterProps.position declares sidebar/inline/drawer (default sidebar) while every renderer read point compares bottom/right/left, and its collapsible default inverts the renderer merge's — value-level declared-but-unread divergence on the row now shared by chatter and discussion",
        "filed as objectui#4663: record:quick_actions renderer reads aria.label (the one spelling the platform ARIA contract refuses) and the registration's actionNames description claims an empty-bar fallback the renderer does not implement"
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  9. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor

    ACCEPT (spec seat, session session_01E5tUwGM3LQoqErTfkvRW7W) — PR #8773.

    Takeover outcome first, because it is the part that could have gone badly: a container restart killed the dev mid-verification, after committing and before pushing — the one genuinely fragile state a dev can be in, with 17 files of work living in a single worktree. Resumed from transcript with a priority-ordered instruction (push first, then report honestly how far verification actually got, then finish the gaps). It did exactly that: branch pushed on resume, and the three incomplete legs — the restore-rebuild, the full suites at HEAD, the gate union — were finished afterwards rather than claimed. Zero work lost, zero fabricated runs.

    On substance, the review's highlight is that the registrations' declared-input lists failed verification in both directions — which is precisely why the triage note called them a claim rather than a measurement:

    • record:quick_actions: the renderer reads aria.label, the one spelling the platform ARIA contract refuses ⇒ guidance refusal, not a declared key (filed upstream as objectui#4663, verified filed). actions, and history's entries/loading, are host-synthesizer channels ⇒ refused with prescriptions.
    • record:alert: icon is read here and is declared — the opposite verdict from the rail on the same-named key. That contrast is the whole argument for measuring per component instead of generalising spec: record:reference_rail has no ComponentPropsMap row — an entry filter parses, typechecks, validates, ships, and silently does nothing #8691's finding; had this card inherited the rail's answer it would have been wrong.
    • title / body / action.label are I18nLabelSchema because pickLocalized was measured on them, not assumed.

    Binding condition 2 (the pair) was honoured and corrected the card on the way: record:discussion needs a row, and the right row is record:chatter's own schema object — one renderer, one accept face — while the card's "discussion is palette tier A" framing is inverted at the pin (the palette carries chatter). Conclusion unchanged, premise repaired. The shared row's own position vocabulary / collapsible default divergence from the renderer was filed, not fixed (#8762, already graded needs-user-decision by triage) — correct call: that is a value-level accept-face question, not this card's key-level one.

    Ablation proved on the built artifact in both legs (marker absent across 65 built files → the four "reports" lint pins red with zero findings, i.e. pre-fix silence, while the two "stays silent" controls held; restore → marker present → all green). Suites: spec 10638, lint 2031, turbo build 70/70 downstream. Registry at 89 semantic after merging main.

    Ready + auto-merge armed; the queue gates on the remaining conclusions.


    Generated by Claude Code

  10. removed their assignment
    on Aug 15, 2026
  11. yinlianghui commented on Aug 18, 2026

    @yinlianghui
    Contributor

    跨仓解锁通知(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt):本卡当时对 RecordQuickActionsProps 刻意不声明 aria(避免 declared-but-unenforced,#8691 类),条件是「the renderer reads the contract spelling 之日」—— 该条件由 objectui PR #5133(objectui#4663,渲染器读点改 aria?.ariaLabel ?? aria?.label canonical 优先)满足,已验收 auto-merge 在途。follow-up(声明 aria: AriaPropsSchema)可入 objectstack 主队列排期;⚠️ 同时请连同 objectui#5134 一起看 —— AriaPropsSchema.ariaLabel 是 I18nLabelSchema(接受 inline locale map),而 objectui 侧读点以 as string 上 DOM,map 值会念成 [object Object];声明 aria 后 quick_actions 新读点会继承同一空缺,宜一并处理。


    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

    bugSomething isn't workingdomain:specDomain lane — seat card indexed by label:pm:seat

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions