diff --git a/docs/IMPLEMENTATION_LOG.md b/docs/IMPLEMENTATION_LOG.md index 939a189..532cc33 100644 --- a/docs/IMPLEMENTATION_LOG.md +++ b/docs/IMPLEMENTATION_LOG.md @@ -1053,3 +1053,36 @@ Fail-first evidence: pre-contract, recording with the new intent failed with getIntent's "intent 'transactional-review' is not registered"; all four hotel-reservations.spec.ts tests and the rewritten studio-shell assertion failed on the pre-scenario build. + +## 2026-07-21 — example-expansion milestone: close-out + +Merged this milestone, in order: #16 (tour auto-start for first-time +visitors + first-run/studio Playwright projects), #17 (support-triage, +record-collection), #18 (signup/onboarding, data-collection), #19 +(selection vocabulary: list, selectable-card, metadata-list; x-drift +acknowledged-divergence lane in drift-check), #20 (transactional-review ++ hotel-reservations). Cross-repo: dspack-gen 0.1.2 published +(contract-declared required props reach the constrained-decoding +grammar; dspack-gen #43), consumed from npm. + +Final verified state on main at 7db93d8: 6 of 6 scenarios ready, none +planned; Astryx contract at 12 components / 6 intents / 14 rules / 6 +worked examples; fixtures 001-016 (004 bundled-unreferenced, +pre-existing); every scenario replay/permalink/X-ray/Break-It/Restyle +capable with live-recorded fixtures and fail-first e2e in both suites. +Gates at close: pnpm -r typecheck/test clean; contracts build:catalogs ++ drift-check clean; local Playwright 106 passed / 4 skipped; +production config against the deploy artifact 86 passed; live agent +runs verified for record-collection, data-collection, and +transactional-review on the published generator. + +Deliberate deferrals, now platform work (roadmap rewritten in +docs/PHASE-NEXT.md): contract re-sync upstream to dspack/examples (the +byte-sync constraint is currently unmet — the milestone authored +governance in the studio copy through reviewed PRs while upstream still +carries the 3-intent edition; P0), shadcn contract parity (8/1/5 vs +12/6/14; P1), transactional-review interactivity (P2), generator +expressiveness ceilings (requireOneOf, cardinality, per-parent child +grammars; P3), dropdown-menu item vocabulary (P4), search-class +components (P5). Technical debt introduced: none identified beyond +those declared deferrals. diff --git a/docs/PHASE-NEXT.md b/docs/PHASE-NEXT.md index ac59490..fd29b4c 100644 --- a/docs/PHASE-NEXT.md +++ b/docs/PHASE-NEXT.md @@ -1,34 +1,45 @@ -# Phase Next: the post-launch roadmap - -The original plan's Phases 1 through 5 are complete (see docs/AUDIT.md); -FM-1 through FM-6, FM-8, FM-9, and FM-12 are shipped. npm publishing across -the ecosystem is resolved: dspack-gen 0.1.1, dspack-emit 0.3.1, and ds-mcp -0.3.0 are published, match their repos, and the studio consumes the -published versions. This phase covers, in priority order: - -0. **WS0** — precondition: ds-mcp vendored-core re-pin (lands in the ds-mcp - repo; blocks every take-it-home reference to ds-mcp) -1. **WS1** — FM-11 take-it-home -2. **WS2** — FM-10 groundwork: design-system swap, engineering track -3. **WS3** — FM-7 completion: the HITL question as a real governed surface - -Two cross-repo dependencies gate this phase and are stated where they bind: -the **ds-mcp re-pin and 0.3.1 release** (WS0, blocks WS1 task 1) and the -**owner-authored shadcn contract extension upstream in `dspack/examples`** -(WS2, its PR sequences before the studio's byte-sync). +# Phase Next: the platform roadmap + +Rewritten 2026-07-21 at the close of the example-expansion milestone. The +previous edition of this document planned WS0 through WS3 (ds-mcp re-pin, +FM-11 take-it-home, FM-10 design-system swap groundwork, FM-7 governed +question); all four shipped and merged, followed by the example-expansion +milestone itself (PRs #16 through #20 plus dspack-gen 0.1.2). Delivered +state, verified on main at `7db93d8`: + +- **6 of 6 scenarios ready, none planned**: recipe-creator, + appointment-booking, project-deletion, support-triage, onboarding, + hotel-reservations. Full history in `docs/IMPLEMENTATION_LOG.md`; + audit posture in `docs/AUDIT.md`. +- **Astryx contract**: 12 components, 6 intents, 14 rules, 6 worked + examples. New this milestone: intents record-collection, + data-collection, transactional-review; components list, + selectable-card, metadata-list; the tour auto-starts for first-time + visitors. +- **dspack-gen 0.1.2** published: contract-declared `required: true` + props reach the constrained-decoding grammar (born from measured + data-less tables across ~20 live runs on two models). + +Everything remaining is **platform work, not scenario work**. Items are +ordered by leverage; each is deliberate deferral, not drift, and none +blocks the shipped experience. ## Standing constraints (restated, non-negotiable) - Honest magic only. Nothing simulated or synthesized; replays are recorded real runs; capabilities that need the local agent say so plainly. - No credentials ever transit the browser. -- Import isolation: `@a2ui/*` only in `a2ui-ingest` and `astryx-renderers`, - `@ag-ui/*` only in `agui-bridge`, design-system imports only in their own - renderer package. +- Import isolation: `@a2ui/*` only in `a2ui-ingest` and the renderer + packages' shared helper, `@ag-ui/*` only in `agui-bridge`, design-system + imports only in their own renderer package. - Published packages are consumed from npm, never vendored, with the single documented exception of ds-mcp's build-time core bundle. -- `packages/contracts/astryx.dspack.json` byte-syncs with dspack main and is - never edited locally. The same discipline extends to any second contract. +- `packages/contracts/astryx.dspack.json` byte-syncs with dspack main. + **This constraint is currently unmet** — see P0. The example-expansion + milestone authored governance (3 intents, 9 rules, 3 examples, 3 + components) directly in the studio copy through reviewed PRs; upstream + `dspack/examples/astryx.dspack.json` still carries the 3-intent, + 9-component edition. Restoring the sync is the roadmap's first item. - All governance content (intents, rules, rationales, examples) is owner-authored. This document specifies required shapes only. - Public-facing copy follows AF brand voice: declarative, unhyped, @@ -36,447 +47,92 @@ the **ds-mcp re-pin and 0.3.1 release** (WS0, blocks WS1 task 1) and the ## Delivery discipline -Each workstream ships as its own review-gated PR (WS0 in the ds-mcp repo). -Every new assertion lands fail-first: written against the old code, run, -the failure output pasted into the PR body, then the change, then the same -assertions passing. Specs are never edited to green in the same step that -changes the behavior under them. - ---- - -## WS0 — Precondition: ds-mcp vendored-core re-pin - -ds-mcp vendors `@aestheticfunction/dspack-gen/core` at build time -(`npm run bundle:core` → `dist/vendor/dspack-gen-core.js`) from a -commit-pinned devDependency. The current pin is `f651433`, which predates -dspack-gen's v0.1.1 fix `f987aed` ("align the generation schema with -grammar-constrained decoders", `src/core/generation-schema.ts`). The -published ds-mcp 0.3.0 therefore serves the pre-fix generation schema from -`get-generation-context`: array-typed contract props degraded to strings, -and node `text` declared before `props`, the exact defects 0.1.1 fixed. -Its validation semantics can diverge from the studio, which runs gen 0.1.1. -This work lands in the **ds-mcp repo**, follows its documented re-pin -ritual (README, "dspack-gen is a build-time dependency"), and ships as -ds-mcp 0.3.1. - -**Cross-repo sequencing:** WS0 is step zero of the whole phase. No -take-it-home copy in the studio may reference ds-mcp until 0.3.1 is live on -npm. - -### Tasks - -| # | Task | Touch points (ds-mcp repo) | Gating | -|---|---|---|---| -| 0.1 | Golden-context honesty test, fail-first | `src/tests/generation-context-golden.test.ts` (new), `src/tests/fixtures/shadcn.destructive-action.context.json` (new synced copy), `scripts/check-sync.mjs` (manifest entry) | agent-executable | -| 0.2 | Re-pin to the v0.1.1 tag commit, rebuild | `package.json` (devDependency → `374e1cd`), `package-lock.json`, `dist/vendor/dspack-gen-core.js` (regenerated) | agent-executable | -| 0.3 | Report-only pin-drift CI check | `scripts/check-core-pin.mjs` (new), CI workflow (new job) | agent-executable | -| 0.4 | Release 0.3.1 | `package.json` version, `CHANGELOG.md`, npm publish | owner-gated (publish + PR merge) | -| 0.5 | Optional: trusted publishing | release workflow (new), matching dspack-gen's | agent-executable except repo settings + npm trusted-publisher configuration (owner-gated) | -| 0.6 | Post-publish verification | none (registry install in a temp dir) | agent-executable after 0.4 | - -**0.1 Golden-context honesty test.** dspack-gen ships -`fixtures/golden/context/shadcn.destructive-action.json`, regenerated by -the 0.1.1 fix — the natural oracle. The test builds -`getGenerationContext(doc, { intent: "destructive-action" })` against -`examples/shadcn-ui-v04.dspack.json` (already byte-synced from dspack main) -and asserts the served context matches a synced copy of that golden. The -golden copy joins the `check-sync.mjs` MANIFEST (source: dspack-gen main -raw URL) so drift stays loud, the same deliberate validator-semantics -coupling the F1 lint goldens already establish. Fail-first: run against -the current `f651433` bundle first; it must fail on the pre-fix schema -(string-typed array props, text-before-props ordering); paste the failure -output into the PR. - -**0.2 Re-pin.** Pin the devDependency to `374e1cd`, the v0.1.1 tag commit, -not past it. Verified geometry: `git diff --name-only v0.1.1 HEAD -- src/core/` -in dspack-gen shows only `src/core/grammar-alignment.test.ts` (the -regression-test commit `d9e1f24`), and test files never ship in the esbuild -vendor bundle, so pinning past the tag buys nothing. Pinning the tag makes -the invariant "the vendored core corresponds exactly to a published -dspack-gen version" and keeps the drift check (0.3) green by construction. -Rebuild (`npm run build` runs tsc + `bundle:core`), full test suite green -including the new golden test. - -**0.3 Pin-drift CI check.** A report-only job in the spirit of the studio's -Astryx drift check: `scripts/check-core-pin.mjs` resolves dspack-gen's -latest release tag (GitHub API), diffs `src/core/**` between the pinned -commit and that tag, and fails loudly when files differ. `*.test.ts` files -are excluded from the diff — the documented escape for intentional -ahead-of-release pins — but the default posture is tag-pinned, where the -check is green by construction. On GitHub API failure the job logs and -tolerates (report-only means the check never blocks unrelated work on -infrastructure noise); a real content diff always fails. - -**0.5 Optional: trusted publishing.** Move ds-mcp's npm publish to GitHub -Actions trusted publishing in the 0.3.1 release, matching dspack-gen's -release workflow. This makes the pin-drift check actionable end to end: a -red check leads to a re-pin PR, and the release is one merge away. Workflow -code is agent-executable; the repo settings and npm trusted-publisher -registration are owner-gated. - -**0.6 Post-publish verification.** Install `@aestheticfunction/ds-mcp@0.3.1` -from the registry in a temp dir, run `get-generation-context` for -`destructive-action`, and byte-compare the served schema against dspack-gen -0.1.1's golden. Record the output in the release notes. - -### New tests - -- Unit (ds-mcp): the golden-context test (0.1) — the honesty check in the - spirit of the studio's receipts byte-match: what ds-mcp serves is what - dspack-gen 0.1.1 computes, byte for byte. -- CI: the pin-drift job (0.3). -- Manual-but-recorded: the post-publish registry verification (0.6). - -### Risks - -- **The golden couples ds-mcp CI to dspack-gen main.** Deliberate, same as - the existing F1 goldens: a red sync check means the pin should move. - Mitigation: the pin-drift job points at the fix (re-pin, rebuild, release). -- **GitHub API rate limits in the drift job.** Mitigation: report-only - posture tolerates API failure with a logged warning; only a real diff - fails the job. -- **Registry propagation lag after publish** (seen with dspack-gen 0.1.1). - Mitigation: 0.6 waits and verifies before anything downstream references - 0.3.1. - -### Exit criteria - -- ds-mcp 0.3.1 on npm; a registry install serves a generation schema - byte-matching dspack-gen 0.1.1's golden context fixture (0.6 recorded). -- The golden-context test exists with fail-first evidence in the PR - (failing output against `f651433`, passing against `374e1cd`). -- check-sync green with the new golden manifest entry; the pin-drift CI job - green against the v0.1.1 tag. -- ds-mcp README's re-pin ritual updated to name the drift check and the - tag-pinned default posture. - ---- - -## WS1 — FM-11: take-it-home - -A visitor who liked the studio can adopt the ecosystem in their own editor. -The experience lives in a new take-it-home view in the studio shell, -alongside replay / live / break / restyle. - -Depends on WS0 for anything that names ds-mcp. Tasks 1.2 and 1.4 can -proceed in parallel with WS0; nothing ships to the deployed site -referencing ds-mcp until 0.3.1 is live. - -### Tasks - -| # | Task | Touch points | Gating | -|---|---|---|---| -| 1.1 | Downloadable ds-mcp MCP config | `apps/web/app/take-home-view.tsx` (new), `apps/web/app/studio.tsx` (view wiring, `viewHelp`), `apps/web/app/permalink.ts` (panel support) | agent-executable, blocked on WS0 | -| 1.2 | Live validate-ui box, client-side spike first | `apps/web/package.json` (add `@aestheticfunction/dspack-gen`, `/core` subpath only), `apps/web/app/take-home-view.tsx`, contract JSON already shipped by `packages/contracts` | agent-executable | -| 1.3 | Local-agent path: two options, choice owner-gated | docs + `take-home-view.tsx` copy; option (b) additionally `apps/agent` packaging | choice owner-gated; see below | -| 1.4 | Brand-voice pass on all take-home copy | `take-home-view.tsx` | agent-executable | - -**1.1 MCP config download.** A copy-paste and downloadable MCP client -config running `npx -y @aestheticfunction/ds-mcp@^0.3.1` with `--dspack` -pointing at a downloadable copy of the studio's byte-synced astryx -contract (offered for download beside the config; the upstream shadcn -example is linked as the alternative corpus). The config references -**0.3.1 or later only** — this is why WS0 is step zero. - -**1.2 Live validate-ui box.** The box runs the real validator against a -pasted surface. Client-side execution against the published packages is -evaluated first, because the static Cloudflare deploy is the primary -surface and the break-it offline pattern established that nothing -dead-ends silently on the static site. Feasibility evidence: dspack-gen's -`/core` subpath is enforced zero-network and emitter-free by its own -`core-boundary.test.ts`; `lintSurface` (S1/S2/S3) is pure computation; ajv -is browser-safe; the byte-synced contract already ships with the studio. -The spike — bundle `/core` in the Next static export and lint a surface -in-browser — is the first task of this workstream. If client-side works: -paste a dspack surface, get the findings object rendered with the same -finding presentation the gates tab uses, entirely on the static site. If -it does not: the box states its live-only status plainly, following the -break-mode convention (it says what it needs; it never dead-ends -silently). Import isolation is unaffected: the isolation rules cover -`@a2ui/*`, `@ag-ui/*`, and design systems; dspack-gen is a published npm -package consumed normally. - -**1.3 Local-agent path.** Running "run it live" at home needs -`apps/agent`. Two options, presented side by side; the choice is -owner-gated because option (b) is a permanent product decision (a name -claim, a release process, a second artifact that can drift from the repo): - -- **(a) Clone-and-run, the launch-day path.** Copy-paste block: clone the - repo, `pnpm install && pnpm build:contracts && pnpm --filter agent dev`, - point the studio (deployed or local) at `localhost:8787`. Visitor - experience: three commands, needs git + pnpm + node. Maintenance cost: - none beyond the repo itself; the instructions are the README's own dev - flow. Zero new publishing surface. -- **(b) Published npx package, the fast-follow.** Publish - `@aestheticfunction/dspack-studio-agent`: a bin build of `apps/agent` - with the repo's own workspace packages (`agui-bridge`, `contracts`, - `replay`) compiled in — same-repo source, not third-party vendoring, and - documented as such — while dspack-gen and dspack-emit remain regular npm - dependencies. Visitor experience: one command - (`npx @aestheticfunction/dspack-studio-agent`). Maintenance cost: an npm - name claim, a release process (version, changelog, publish per studio - change that touches the agent), and drift risk between the published - artifact and the repo. Triggers for choosing (b): measured take-home - adoption where the clone path is the observed drop-off point; or a - studio release cadence stable enough that the agent artifact rarely - moves. - -No WS1 task sequences behind this choice except the gated npx e2e spec for -option (b) itself (launch the published package via npx in a temp dir, -drive a scripted run end to end). Option (a)'s instructions are verified -end to end by an e2e-adjacent script that executes the copy-paste block -verbatim in a clean checkout. - -### New tests - -- Unit: config-generator output pins `^0.3.1` or later (a literal - assertion, so a future copy edit cannot silently reference 0.3.0); - validate-box wrapper (findings pass through unmodified, parse errors - reported verbatim). -- Playwright, the honesty check in the receipts spirit: paste - fixture-001's recorded attempt-0 violating surface into the validate box - and assert the client-side findings match the recorded run's gate - findings — same rule ids, same messages. The browser validator agrees - with the recorded pipeline, provably. -- Playwright, break-offline style: the take-home view with the agent - blocked at the network layer — the MCP config and validate box (if - client-side) fully work; anything agent-dependent states it plainly. -- Playwright: axe (wcag2a/2aa) coverage for the new view; registration in - the production smoke suite. -- Gated (option b only): the npx e2e spec. - -### Risks - -- **`/core` does not bundle under the Next static export.** Mitigation: - the spike is first; the honest fallback (live-only, stated plainly) is - specified in advance, so the workstream cannot stall on it. -- **Copy references drift as versions move.** Mitigation: version strings - in take-home copy are read from one constants module and unit-asserted. -- **Option (b) publish decisions stall the view.** Mitigation: the view - ships with option (a); (b) is additive. - -### Exit criteria - -- A visitor on the deployed static site can: download a working MCP config - referencing ds-mcp 0.3.1+; validate a pasted surface client-side (or - read a plainly-labeled live-only box, if the spike falsified - feasibility); follow a copy-paste local-agent path that has been - executed end to end, not just written. -- The fixture-001 findings-agreement honesty test passes. -- No copy anywhere in the view references an unpublished version or - capability. -- All copy passes the AF voice check (declarative, unhyped, no em-dashes). - ---- - -## WS2 — FM-10 groundwork: design-system swap, engineering track - -The goal of this workstream is that everything except the contract itself -can proceed. It is grounded in the boundaries stated in -docs/renderer-abstraction.md and verified by -`packages/a2ui-ingest/src/registry-abstraction.test.ts`: the registry -supplies `{ reuseBasic, custom }` and nothing else; the catalog is the -sole source of vocabulary; incremental adoption via the `unimplemented` -placeholder path is the intended migration mechanism. - -**Cross-repo dependency (flagged):** contracts byte-sync with dspack main, -so the shadcn dspack contract lives upstream in `dspack/examples` and that -PR sequences first. The contract is owner-authored governance content and -out of scope here; it is treated as an input with a placeholder. - -**Required shape of the input contract** (for this plan to execute): - -- A component list covering the studio catalog's vocabulary needs: button, - text, text input, card, stack/column, alert dialog, table, and badge - equivalents. (The existing upstream `dspack/examples/shadcn-ui.dspack.json` - carries 8 components; coverage against this list is the owner's call.) -- Coverage across all four rule types: component-choice, - forbidden-composition, required-composition, required-props. (The - existing upstream contract already exercises all four.) -- Intent-tagged examples for every intent the swap demo covers: - destructive-action at minimum; scheduling and structured-editing if the - interactive scenarios swap too. (The existing contract has - destructive-action only, with one example.) - -Until the extended contract lands, all engineering tasks below validate -against a placeholder test catalog, the pattern -`registry-abstraction.test.ts` already established. - -### Tasks - -| # | Task | Touch points | Gating | -|---|---|---|---| -| 2.1 | `packages/shadcn-renderers` | new package: `src/registry.tsx`, `src/components/*`, `src/themes.ts`, package.json | agent-executable | -| 2.2 | Second-catalog contract build | `packages/contracts/src/build.ts` (emit via dspack-emit's existing `shadcnProfile`), `packages/contracts/package.json` (contract copy), CI byte-sync check for the new copy | wiring agent-executable; final emission blocked on the upstream contract | -| 2.3 | Swap UI | `apps/web/app/restyle-view.tsx` (design-system selector), `apps/web/app/studio.tsx` (a `DesignSystem` config `{ catalog, registry, theming }` threaded to `A2uiCanvas`) | agent-executable | -| 2.4 | CI additions | ci.yml: second-catalog A1/A2/A3 gates, second byte-sync check, unimplemented-coverage report | agent-executable | - -**2.1 The registry package.** Exports exactly a -`Registry { reuseBasic, custom }` plus its own theming wiring; shadcn and -its dependency imports are confined to this package (the import-isolation -rule extends to it). Catalog names without a visual render the -`makeUnimplemented` placeholder: legal vocabulary, missing pixels, run -otherwise unaffected — incremental adoption is the intended mechanism, so -the package is shippable at any coverage level and its coverage is -reported, not hidden. - -**2.2 The build.** dspack-emit already exports `shadcnProfile` -(`src/transform/profiles.ts`); `packages/contracts/src/build.ts` -generalizes to emit a second gated catalog from the synced shadcn -contract. The contract copy is byte-synced from dspack main and never -edited locally, identical discipline to the astryx copy. - -**2.3 The swap UI.** A design-system selector in the restyle view (FM-10 -is the restyle story completed: themes change pixels within one design -system; the swap changes the design system). Selecting a system swaps -`{ catalog, registry, theming }` behind `A2uiCanvas(catalog, registry, -messages)`; application logic (RunView, inspectors, scenarios, actions, -replay) is untouched, per the renderer-abstraction contract. The second -registry is async-loaded so the primary bundle does not grow. - -### New tests - -- Unit: registry-abstraction parity suite for the shadcn registry — names - come from the catalog alone; `unimplemented` is the exact complement of - the registry; catalog-derived schemas accept contract-valid props and - reject unknown ones. Runs against the placeholder catalog until the real - one lands, then against both. -- Playwright, the honesty check: replay the same fixture under both design - systems and assert the downloaded **receipt hash is identical** — - structure, events, gates, and audit unchanged; only pixels differ. FM-5's - claim ("nothing about this interface changed") generalized to FM-10. - Plus: an unimplemented component renders the visible placeholder, and - the run completes. -- CI: both catalogs gate A1/A2/A3; both contract copies byte-match dspack - main; the coverage report lists placeholder components loudly. - -### Risks - -- **Upstream contract timing.** Mitigation: the placeholder path is the - intended migration mechanism; the swap UI can ship honestly against a - partial registry, stating coverage. The contract PR is tracked as the - single blocker to full coverage. -- **shadcn dependency weight in the static bundle.** Mitigation: - async-load the second registry the way theme CSS already loads; measure - against the current 2.1 MB app-chunk baseline before accepting. -- **Two contracts drift apart in vocabulary.** Mitigation: they are - allowed to — the catalog owns vocabulary per design system; the parity - suite asserts the mechanism, not identical component lists. - -### Exit criteria - -- A second registry package renders a governed run with visible - placeholders for unimplemented names, at whatever coverage the input - contract supports. -- Swapping design systems on a replayed fixture changes pixels and nothing - else, proven by the receipt-hash-identical Playwright test. -- CI gates both catalogs, byte-syncs both contracts, and reports registry - coverage. -- The upstream contract dependency is explicitly tracked (dspack PR link - in the studio issue) as the only remaining blocker to full coverage. - ---- - -## WS3 — FM-7 completion: the HITL question as a real governed surface - -The appointment scenario pauses for a human decision, but the agent's -question is not itself a governed surface. FM-7 completes when the -question ("Book {slot} for {name}?") renders as a governed AlertDialog -through the ordinary pipeline. Not a synthesized surface: the question -must be a real generated, gated surface or the feature waits. - -### Tasks - -| # | Task | Touch points | Gating | -|---|---|---|---| -| 3.1 | Deterministic question path (CI-stable) | `apps/agent/src/scenarios/appointment-booking.ts`, an authored question surface, the scripted-adapter path `break-scripts.ts` established | agent-executable | -| 3.2 | Live question path | `apps/agent/src/scenarios/appointment-booking.ts`, `apps/agent/src/server.ts`, `packages/scenarios/capabilities.ts` | agent-executable | -| 3.3 | fixture-007 recording | gated RECORD_LIVE Playwright flow, `packages/scenarios/registry.ts` | agent-executable (recording is gated/manual by design) | - -**3.1 Deterministic path.** The break-scripts precedent applies directly: -an authored surface played through the ORDINARY pipeline by the scripted -adapter — real S1/S2/S3 gates, real emission, honestly labeled -`mode: "scripted"`. The question surface is authored under the existing -`scheduling` intent (scenario surfaces are engineering-authored, like -`surfaces/appointment-booking.dsurface.json`; only intents, rules, -rationales, and contract examples are owner-authored). The unscoped rules -govern it, and the story tells itself: -`rule.alertdialog-action-label-specific` forbids a vague "OK" on the -question's confirm button — the HITL question is subject to the same -governance as everything else. On select_slot acceptance, the responder -runs this pipeline and the delivered AlertDialog carries the -confirm/cancel actions (grounded to `confirm_booking` / `cancel_booking`). - -**3.2 Live path.** With a model selected, select_slot acceptance triggers -a real `runPipeline` run under `scheduling` with a question prompt; the -generated surface is gated, delivered, and grounded by the established -enhancement discipline: unambiguous-only, never label-string guessing -beyond validated semantics, every attachment recorded in the stream via -`studio.surface.enhanced`. Ungroundable question surfaces reject clearly -and the flow falls back to the deterministic question, stated in the -stream. - -**3.3 fixture-007.** Record the full live arc — select a slot, the -generated gated question surface, confirm, booked — via the gated -RECORD_LIVE flow with the standard retry budget. Register it on the shelf. - -**Owner-gated boundary (specified, not drafted):** if measurement shows -the `scheduling` intent cannot carry the question surface (models refuse -AlertDialog under it, or a rule gap emerges), the fix is owner-authored -governance content. Required shape, should it come to that: an intent -(id + one-sentence description, e.g. a confirmation-question intent) and -one worked example (a question AlertDialog surface, verbatim). Per the -fixture-006 discipline: a measured failure signature first; retries are -not the fix; the feature waits rather than synthesizing. - -### New tests - -- Agent unit tests: question-surface flow (select_slot acceptance produces - a gated delivery; confirm/cancel on the question resolve to the booking - responders; the deterministic fallback path). -- Playwright, deterministic: the question surface's OWN gate results - appear in the gate ticker; the rendered question is an AlertDialog; the - confirm label is specific (asserting the governed content, not just - presence); scrub-back reconstruction through the question moment; - fixture-007 shelf replay once recorded. -- Fail-first evidence for every changed booking-flow assertion (the - existing interactive specs pin the current pause behavior; their new - expected assertions must fail on the old code first, output pasted). - -### Risks - -- **Generated question surfaces vary or fail grounding.** Mitigation: - unambiguous-only grounding rejects clearly; deterministic fallback is - honest and recorded in the stream; recorder retry budget as with - fixture-006. -- **The scheduling intent does not steer models to an AlertDialog - question.** Mitigation: measure first (the fixture-006 discipline); if - the signature shows a governance gap, document the owner-shaped proposal - above and wait. The deterministic path still completes FM-7's rendering - claim honestly (real gates, labeled scripted) while the live path waits. -- **The second surface complicates replay reconstruction.** Mitigation: - surfaces are already keyed by surfaceId through the reducers; the - scrub-back e2e through the question moment is the proof. - -### Exit criteria - -- The booking pause is itself a governed surface: its gate results are on - the record in the ticker and inspectors, deterministic in CI (labeled - scripted, ordinary pipeline) and recorded live as fixture-007. -- The question's confirm affordance demonstrably obeys - `rule.alertdialog-action-label-specific` (asserted in e2e). -- Nothing synthesized: the scripted path is labeled, the live path is a - real run, fallbacks are stated in the stream. -- Scrub-back reconstruction through the question moment passes. - ---- +Unchanged: each work item ships as its own review-gated PR; every new +assertion lands fail-first (written against the old code, run, failing +output pasted into the PR); fixtures ship only from real model runs; +copy is written from recorded events, never the reverse; new intents land +atomically with the scenario or surface that validates them. + +## P0 — Contract re-sync upstream (restores a standing constraint) + +The studio's Astryx contract is the current source of truth in fact but +not in declared direction: the byte-sync discipline names dspack main as +canonical, and the take-home view tells visitors the downloaded contract +"is the byte-synced copy of dspack main". Until upstream catches up, that +user-facing sentence over-claims. + +Work: propagate the studio's `astryx.dspack.json` (and the unchanged +`shadcn-ui.dspack.json`) to `dspack/examples/` via a dspack-repo PR, then +re-assert byte-equality here. Owner decision folded in: whether the sync +direction stays studio-led during active milestones or reverts to +upstream-led between them. Small, mechanical, high honesty value. + +## P1 — shadcn contract parity (owner-authored, upstream) + +The largest declared gap, and older than this milestone: the shadcn +catalog carries 8 components, 1 intent (destructive-action), and 5 rules +against Astryx's 12/6/14. Renderer-level parity is current (11 of 12 +catalog names; Dialog is the deliberate placeholder), so the design-swap +experience is whole; what is missing is governance vocabulary. Parity +means: the 5 newer intents with rules and worked examples appropriate to +shadcn's idioms (its `data-table-with-row-actions` pattern is the natural +seed for record-collection; its category registry likely needs to grow +beyond interactive/overlay), plus selection-class components mirroring +list / selectable-card / metadata-list. Generation always lints under the +Astryx catalog, so this work changes what shadcn *governs*, not what the +studio runs. + +## P2 — Transactional-review interactivity (select → confirm) + +Designed during the vocabulary milestone; needs no platform extension. +Shape: `transactionalCapabilities` with `select_option` grounded by +enhancer-attached `context.optionId` (matchers see only the source +component, so ancestry rides the enhancement, exactly as booking grounds +its overlay), `confirm_reservation` through the FM-7 governed question, +an agent responder holding `/reservation/*` state, an authored +`.dsurface.json` start, and `isSelected` re-delivery for visual +selection. A matcher-ancestry extension becomes relevant only if +raw-generated live interactivity without enhancement is ever wanted. + +## P3 — Generator expressiveness (dspack-gen 0.2 candidates) + +Take up only when a scenario demonstrably needs enforcement beyond +floors; nothing shipped requires these, and each ceiling is stated inside +the affected rules' rationales today: + +- `requireOneOf` choice sets (component-choice `require` is conjunctive; + the record-collection table rule documents the inexpressible + table-or-list widening). +- Cardinality ceilings and exact counts ("exactly one primary action per + option" is prose guidance today). +- Per-parent child grammars (the generation schema's documented + spike-inherited simplification: children accept the full vocabulary at + every level; per-parent constraints are S3 territory). + +## P4 — dropdown-menu item vocabulary + +The one casualty in both catalogs: its `items` array has no declared item +shape, so both profiles refuse it, and two shipped fixtures document the +refusal honestly (project-deletion's fixture-003, onboarding's +fixture-014). Specifying the item vocabulary (labels, actions, +separators) in the contract unlocks the shadcn `contextual-actions-menu` +and `data-table-with-row-actions` patterns and retires the standing +casualty entry in `astryx-profile.ts`. + +## P5 — Search-class vocabulary (optional) + +Deliberately out of the minimal selection slice: upstream Astryx already +ships Typeahead, PowerSearch, DateRangeInput, and Calendar. Admit them +only when a scenario needs live querying or date selection rather than a +prompt-stated ask; the vocabulary-addition path (contract entry, profile +mapping, drift-check name, renderers in both design systems, `x-drift` +for any documented divergence) is established and mechanical. ## The first task -The single highest-leverage first task, the way the original plan named -fixture #1: **the golden-context assertion that fails on the stale pin** -(WS0 task 0.1). Write ds-mcp's `get-generation-context` golden test -against dspack-gen 0.1.1's context fixture and run it against the current -`f651433` bundle. Its failure output is the measured signature that -justifies everything downstream — the re-pin, the 0.3.1 release, the -pin-drift check, and every take-it-home reference to ds-mcp. +As always, the highest-leverage first task is the one that restores a +stated invariant: **P0's byte-equality check**. Sync the contract +upstream, then add the studio-side assertion that +`packages/contracts/astryx.dspack.json` byte-matches the dspack-main +copy, failing output first. Everything else on this roadmap can proceed +in any order after it.