You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec: record:alert / record:quick_actions / record:history have no ComponentPropsMap row — same silent no-op mechanism #8691 closed for the rail #8744
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: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.
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:
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.
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.
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.
{
"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"
]
}
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.
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.
Activity
hotlong commentedon Aug 14, 2026
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'sComponentPropsMap, and declaring a row changes the accept face — keys that ship silently today start drawingcomponent-props-unknown-key. Under the one-package-three-seats split that is the semantic seat, unconditionally: ⛔ any card that changes accept/reject behaviour isdomain:spec, however small. Consequence at dispatch:model: claude-fable-5is 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:
.objectui-shapin. The registration's declared inputs (severity/title/body/visible/icon/action/dismissible/dismissKeyonrecord:alert;limit/emptyText/unknownUserTextonrecord: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.record:discussion/record:chattertogether, as the card advises:chatterhas 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.pm:dispatchedright now and is editing this exact map. Two agents adding rows toComponentPropsMapin 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
os-zhuang commentedon Aug 14, 2026
Claim: PM loop round 6
Session:
session_01E5tUwGM3LQoqErTfkvRW7WBranch:
claude/issue-8744-record-component-props-rowsWorktree:
objectstack-issue-8744Domain:
domain:specFile surface:
packages/spec/src/ui/component.zod.ts(theComponentPropsMaprows + entry types), new pin test(s) besidecomponent-reference-rail.test.ts,packages/lintdispatch 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 (
730fd9acdonorigin/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 touchespackages/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
os-zhuang commentedon Aug 14, 2026
{ "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
os-zhuang commentedon Aug 14, 2026
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 readsaria.label, the one spelling the platform ARIA contract refuses ⇒ guidance refusal, not a declared key (filed upstream as objectui#4663, verified filed).actions, andhistory'sentries/loading, are host-synthesizer channels ⇒ refused with prescriptions.record:alert:iconis 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_railhas no ComponentPropsMap row — an entryfilterparses, 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.labelareI18nLabelSchemabecausepickLocalizedwas measured on them, not assumed.Binding condition 2 (the pair) was honoured and corrected the card on the way:
record:discussionneeds a row, and the right row isrecord: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 ownpositionvocabulary /collapsibledefault divergence from the renderer was filed, not fixed (#8762, already gradedneeds-user-decisionby 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
feat(spec): declare `record:alert` / `record:quick_actions` / `record…
fix(spec): record:chatter / record:discussion position speaks the ren…
yinlianghui commentedon Aug 18, 2026
跨仓解锁通知(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt):本卡当时对⚠️ 同时请连同 objectui#5134 一起看 ——
RecordQuickActionsProps刻意不声明aria(避免 declared-but-unenforced,#8691 类),条件是「the renderer reads the contract spelling 之日」—— 该条件由 objectui PR #5133(objectui#4663,渲染器读点改aria?.ariaLabel ?? aria?.labelcanonical 优先)满足,已验收 auto-merge 在途。follow-up(声明aria: AriaPropsSchema)可入 objectstack 主队列排期;AriaPropsSchema.ariaLabel是I18nLabelSchema(接受 inline locale map),而 objectui 侧读点以as string上 DOM,map 值会念成 [object Object];声明 aria 后 quick_actions 新读点会继承同一空缺,宜一并处理。Generated by Claude Code
feat(spec): declare cloud-connection:panel / marketplace:installed-li…
feat(spec): declare cloud-connection:panel / marketplace:installed-li…
fix(spec): os migrate meta guidance for the ui-* and plugin-* migrati…
feat(spec): ComponentPropsMap rows for action:button/group/menu/icon …
test(spec): the second ui/ file group's test titles state each cited …