PR: #452: feat(page-card): components + dto_bindings + gen:catalog validation [Story-1791] by @ankitmaheshwari-com
View PR →
Story-1791

Surface-card components + dto_bindings + gen:catalog validation (S-D3)

Story-1785 generated the Page Card authority half (which permissions exist) and explicitly left the component half — what a surface renders, bound to a real wire DTO — as the S-D3 follow-on. This change ships that half as schema + codegen-validation only (no runtime render): the surface card now declares components with a first-class dtoBinding, gen:catalog emits the flattened component catalog, and check:catalog fails CI when a component binds a field the backend does not ship, or names a capability that is not one of the surface's operations.

What changed

Schema: a component is a typed adapter

ListingSurfaceCard gains an optional components[]. Each is { componentId (snake), propSchemaRef, dtoBinding, capabilities }. The dtoBinding is first-class — { dto, fields }, not a bare ref — so the binding is checkable. Absent on forums/clients (no render half yet); present on matters.

Capabilities derive, never re-declare

A component's capabilities reference the surface's operations by key only. The needs permission is resolved from the matching operation at generation time — a component can never claim a permission its operation does not (§7.6 S-D8).

DTO field-set reuse

The validator's DTO field-sets come straight from the generated Zod schema's .shape (matterListItemSchema) — the generated contract is the field-set. No second mirror, no AST parsing.

Second generated artifact

gen:catalog now emits a second committed file, generated-component-catalog.ts — every component flattened with its DTO binding and capabilities resolved to {key, needs} — kept fresh by the same --check gate as the authority artifact.

Where it lives

Verify

pnpm gen:catalog   # emits generated-component-catalog.ts (committed)
pnpm check:catalog # freshness (both artifacts) + field/capability subset
pnpm lint
pnpm typecheck
pnpm test
pnpm build

Coverage added in catalog.test.ts (the two new negative gates) and generated-authority.test.ts (component-catalog freshness).

Non-goal (RFD-AGT-004): schema + codegen-validation only. No reduceFrame, ThreadItemView, the .tsx components, or any wire/render path — those are the render-substrate + 003-components tickets.

Files changed

8 file(s), +354 / -16 lines

Path Role + -
.github/workflows/ci.ymlWorkflow+1-1
scripts/page-card/gen-catalog.tsScript+106-15
src/lib/surface-registry/page-card/cards/matters.card.tsLibrary+23-0
src/lib/surface-registry/page-card/catalog.test.tsLibrary+62-0
src/lib/surface-registry/page-card/catalog.tsLibrary+63-0
src/lib/surface-registry/page-card/generated-authority.test.tsLibrary+7-0
src/lib/surface-registry/page-card/generated-component-catalog.tsLibrary+51-0
src/lib/surface-registry/page-card/listing-surface.schema.tsLibrary+41-0