PR: #455: feat(agents): matter_table + matter_card .tsx behind render-substrate [Story-1795] by @ankitmaheshwari-com
View PR →
Story-1795

003-components: matter_table + matter_card behind the render-substrate

Story-1794 shipped the render-substrate but rendered every valid frame as a text/JSON summary — no typed catalog .tsx existed yet. This change fills in the FIRST real ones: matter_table and matter_card, each bound to the MatterListItem DTO, registered under their snake catalog ids (#1791), and dispatched by ComponentResultView. A render.data frame for those ids now paints a real table / card; an unknown id still falls back to text — the #1794 regression guard (RFD-AGT-004 §D8/§7.6).

What changed

Two typed components, bound to the gen DTO

MatterTable + MatterCard take {rows: MatterListItem[], fields}. Rows come ONLY from the validated frame (no fetch, no row invention); types are reused from src/gen — no hand types.

Columns from the catalog view fields, not hardcoded

resolveRenderComponent reads each component's fields straight off the generated COMPONENT_CATALOG (#1791) and passes them in. The table renders one <th scope="col"> per field; change the catalog field-set and the columns follow.

Dispatch + the regression guard

ComponentResultView resolves the component_id to its .tsx only when fallbackText is null (valid, known rows). Unknown ids / schema-rejected rows arrive as fallbackText and render as text exactly as before — never blank.

a11y

Table = a real <table> with an sr-only caption and scoped column headers (screen-reader navigable). Card = a labelled role="group" region, each row a <section> with a <dl> of label/value pairs.

Two #1794 nits folded

Dropped the unused render_marker field from WireRenderFrame (the app never consumed it; extra wire fields are ignored). Fixed the stale RENDER_CATALOG_VERSION comment that claimed the version is carried to the brain via DockChatContext — it is not; D11 is enforced client-side by validateRenderRows.

No new framework (ponytail)

Reuses the existing Table primitive (src/components/ui/table.tsx) and design tokens. Cell values render generically (String(value), em-dash for nullish) — no per-field formatter framework, no second non-matter shape (that is the 2nd-shape-proof ticket).

Where it lives

Verify

pnpm lint          # clean (1 pre-existing avatar.tsx warning, unrelated)
pnpm test          # 894 passed
pnpm build         # green (tsc + route + catalog codegen)
pnpm check:catalog # OK — 22 L1 surfaces, catalog fresh
git diff --check   # clean
# NOTE: do NOT run pnpm codegen — import contracts from src/gen.

Reversible calls (stated for review): column headers are localized via a new Agents.matterField.* key set; cell values render as their bound DTO string (status enum / ISO date shown as-is) — richer per-field formatting is intentionally deferred (no formatter framework). Card uses <dl> + a display:contents grid wrapper for label/value alignment.

Non-goal (RFD-AGT-004): no second non-matter shape, no producer/emission changes, no new framework.

Files changed

14 file(s), +352 / -20 lines

Path Role + -
messages/en.jsonConfig+12-1
messages/hi.jsonConfig+12-1
src/components/agents/agent-chat-panel.render.test.tsxComponent+31-3
src/components/agents/agent-chat-panel.tsxComponent+27-7
src/components/agents/agent-dock-mount.render.test.tsxComponent+3-2
src/components/agents/render-catalog/fields.tsComponent+21-0
src/components/agents/render-catalog/matter-card.stories.tsxComponent+21-0
src/components/agents/render-catalog/matter-card.tsxComponent+42-0
src/components/agents/render-catalog/matter-table.stories.tsxComponent+25-0
src/components/agents/render-catalog/matter-table.tsxComponent+62-0
src/components/agents/render-catalog/registry.tsComponent+35-0
src/components/agents/render-catalog/render-catalog.fixtures.tsComponent+54-0
src/lib/agents/atlas-rail.tsLibrary+2-2
src/lib/surface-registry/page-card/render-catalog.tsLibrary+5-4