Skip to content

vocabulary: list, selectable-card, metadata-list#19

Merged
ryandmonk merged 1 commit into
mainfrom
feat/vocabulary-selection
Jul 22, 2026
Merged

vocabulary: list, selectable-card, metadata-list#19
ryandmonk merged 1 commit into
mainfrom
feat/vocabulary-selection

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

What

The approved platform slice (PR A of the transactional-selection design): the smallest durable vocabulary expansion for selection & comparison workflows — plan pickers, product comparison, booking options — with nothing hotel-specific and nothing invented. All three components are faithful projections of real upstream Astryx components, verified against npx astryx component --json:

  • list (upstream List) — the non-tabular collection container. Platform-load-bearing: composition minimums only count within a parent component, so list is the only way governance can ever say "at least two options."
  • selectable-card (upstream SelectableCard — "plan pickers, filter chips, or option grids" per its own docs) — the named option unit; isSelected carried as delivered presentational state (selection lives in the data model; no protocol change).
  • metadata-list (upstream MetadataList) — comparable attributes, data-driven (items: [{label, value}], grammar-required via the 0.1.2 mechanism). This is the one deliberate upstream divergence (upstream is children-based; sub-components carry no props under grammar-constrained generation), declared as x-drift on the prop — and drift-check now reports x-drift props as acknowledged divergences instead of findings.

Renderers land in both design systems: Astryx via @astryxdesign/core imports; shadcn as native token-styled visuals (ring-accented option card, definition-list metadata). Dialog remains the sole deliberate shadcn placeholder (11 of 12).

Also revises rule.record-collection-requires-table's caveat honestly: the anticipated "widen to a choice set" is inexpressible (component-choice.require is conjunctive) — uniform records still choose tables, list serves heterogeneous/selectable content, requireOneOf noted as candidate future dspack-gen work.

Deliberately no new intent here. The vocabulary is shared and inert until transactional-review lands in the next PR, atomically with the hotel-reservations scenario that validates it (established no-intent-without-scenario discipline).

Fail-first evidence

  • shadcn registry-parity.test.ts against the 12-name catalog, before the renderers:
× covers exactly the catalog names, with Dialog as the deliberate placeholder
Tests  1 failed | 2 passed (3)
  • drift-check before the x-drift mechanism: metadata-list.items: prop not found on Astryx MetadataList (the genuine, documented divergence).

Gates

  • build:catalogs: all ajv gates green on both catalogs; the emitted catalog grows 9 → 12 (incl. synthesized Column).
  • drift-check: "contract vocabulary matches Astryx docs"; the items divergence reported as acknowledged.
  • pnpm -r typecheck / pnpm -r test green; local Playwright 102 passed / 4 skipped; production config vs the local deploy artifact 82 passed (receipt-hash/design-swap invariance included).
  • Render proof: a scratch comparison surface (card → list → 2 selectable-cards → metadata-lists + badge + button) emitted through the real profile and rendered in both design systems — Astryx shows checkbox-semantics selected state and term/definition metadata pairs; shadcn shows the ring-accented card. Exercised via a throwaway spec (not shipped; the durable render coverage arrives with the scenario spec in PR B).

🤖 Generated with Claude Code

The smallest durable expansion for transactional selection and
comparison — plan pickers, product comparison, booking options — with
nothing hotel-specific and nothing invented: all three are faithful
projections of real upstream Astryx components (verified against
`npx astryx component --json`).

- list (upstream List): the non-tabular collection container. Also the
  only way rules can ever say "at least two options": composition
  minimums count within a parent, never surface-globally.
- selectable-card (upstream SelectableCard, "plan pickers, filter
  chips, or option grids" per its own docs): the named option unit,
  with isSelected carried as delivered presentational state.
- metadata-list (upstream MetadataList): the comparable-attributes
  unit, DATA-DRIVEN ({label,value} items, grammar-required) — the
  Table idiom, because sub-components carry no props under
  grammar-constrained generation. The one deliberate upstream
  divergence, declared as x-drift; drift-check now reports x-drift
  props as acknowledged divergences instead of findings.

Profile mappings project all three through the ordinary emit gates
(catalog grows 9 -> 12 including synthesized Column); renderers land in
BOTH design systems (Astryx via @astryxdesign/core; shadcn as native
token-styled visuals — ring-accented option card, definition-list
metadata). Dialog remains the sole deliberate shadcn placeholder.

Also revises rule.record-collection-requires-table's caveat: the
anticipated "widen to a choice set" is inexpressible (component-choice
require is conjunctive); uniform records still choose tables, list
serves heterogeneous/selectable content, requireOneOf noted as
candidate dspack-gen work.

No new intent yet: the vocabulary is inert until transactional-review
lands with the scenario that validates it (next PR, atomic).

Fail-first: shadcn registry-parity failed against the 12-name catalog
(List/MetadataList/SelectableCard unimplemented) before the renderers
landed; drift-check flagged metadata-list.items before x-drift. Gates:
contracts build:catalogs (all ajv gates) + drift-check clean;
pnpm -r typecheck/test green; local Playwright 102 passed / 4 skipped;
production config vs the local deploy artifact 82 passed. Render proof:
a scratch comparison surface emitted through the real profile renders
in BOTH design systems (selected-state checkbox semantics, metadata
term/definition pairs) — exercised via a throwaway spec, not shipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 23:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Expands the shared contract vocabulary to include list, selectable-card, and metadata-list, and wires up renderer support for both Astryx (@astryxdesign/core) and the shadcn renderer registry (keeping Dialog as the deliberate placeholder), while updating drift-check behavior to treat x-drift props as acknowledged divergences.

Changes:

  • Add List, SelectableCard, and MetadataList to Astryx + shadcn renderer registries, with new renderer implementations for each design system.
  • Extend the contracts catalog/profile to describe the three new components (including metadata-list.items as an acknowledged upstream drift via x-drift).
  • Update parity/e2e/docs/UI notes from “8 of 9” to “11 of 12” / “12 of 12” as appropriate.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shadcn-renderers/src/registry.tsx Registers shadcn renderers for List / SelectableCard / MetadataList and updates coverage note.
packages/shadcn-renderers/src/registry-parity.test.ts Updates expected custom-renderer coverage to include the three new components.
packages/shadcn-renderers/src/components/SelectableCardRender.tsx Adds shadcn visual implementation for SelectableCard.
packages/shadcn-renderers/src/components/MetadataListRender.tsx Adds shadcn visual implementation for MetadataList as a definition list.
packages/shadcn-renderers/src/components/ListRender.tsx Adds shadcn visual implementation for List with density + optional dividers.
packages/contracts/src/drift-check.ts Adds new component name mappings and treats x-drift props as acknowledged divergences.
packages/contracts/src/astryx-profile.ts Adds A2UI projection entries for List / SelectableCard / MetadataList.
packages/contracts/astryx.dspack.json Adds contract catalog entries for list/selectable-card/metadata-list and updates a table-related rule rationale.
packages/astryx-renderers/src/registry.tsx Registers Astryx renderers for List / SelectableCard / MetadataList.
packages/astryx-renderers/src/components/SelectableCardRender.tsx Adds Astryx core SelectableCard projection (presentational onChange).
packages/astryx-renderers/src/components/MetadataListRender.tsx Adds Astryx core MetadataList projection by synthesizing MetadataListItem children from items.
packages/astryx-renderers/src/components/ListRender.tsx Adds Astryx core List projection.
e2e/design-swap.spec.ts Updates the design-system note assertion to “11 of 12”.
docs/renderer-abstraction.md Updates renderer-abstraction doc coverage numbers to reflect the expanded catalog.
apps/web/app/design-system.tsx Updates Astryx/shadcn design system “note” strings to reflect new catalog totals.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

};

export const ListRender: FC<any> = ({ props, buildChild }) => (
<div role="list" className={cn("flex flex-col", GAP[props.density as string] ?? "gap-2", props.hasDividers && "divide-y divide-border [&>*]:pt-2 first:[&>*]:pt-0")}>
Comment on lines +16 to +18
role="option"
aria-selected={selected}
aria-disabled={Boolean(props.isDisabled) || undefined}
@ryandmonk
ryandmonk merged commit 48d0591 into main Jul 22, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants