feat(composer): Build — chat-driven creation from approved components (Phase 3 slice) - #40
Merged
Conversation
The Phase 3 vertical slice, exactly as ratified: select a governed project, describe an interface in chat, generate it from the approved design-system vocabulary (dspack-gen 0.2.0 under the project-scoped contract), watch S1-S3 + bounded repair + emit stream per turn (AG-UI SSE), see it render through the trusted registry, refine it conversationally (the prior surface + the new instruction, regenerated COMPLETELY through every gate, prior turns kept for audit), and accept the result as a contract worked example — which immediately joins that intent's few-shot corpus, so accepted results compound generation. Agent: /project/run gains HttpAgent-shaped bodies + the conversation seed (validated, 400 on malformed) and deterministic scripted twins — a fresh run scripts a contract-derived S2 violation then the intent's LATEST worked example (the governed fail->repair->pass loop with zero models; accepting visibly changes what scripted plays next), a refinement returns the prior surface with a deterministic textual change ONLY when the seed is present (the ratified non-vacuous proof). New /project/save-example: server-side fail-closed acceptance — S1-S3 re-linted on the agent, unknown intents refused, harness + ledger preserved; a disabled client button is a courtesy, this gate is the contract. Governance is never written by builds. Composer: composer-core buildReadiness (exact remaining-setup reasons) + foldBuildEvents + vocabularyGap (S2-evidence only — a gap is named, never invented; the future Component Workshop hook); the Build view (thread, pipeline strip, trusted-registry canvas via the in-browser emit twin, provider/privacy statement, Accept/Refine with locks and focus management); Build-first IA (first nav item, default view when ready, disabled with the exact reason otherwise, Start-building CTA, "Set up your design system, then build with it"). Evidence: agent 40/40 (7 new, fail-first); composer-core 40/40 (6 new, fail-first); DOM suite 34/34 incl. 13 new Build specs (disabled-with- reason, unlock+default, streamed fail->repair->pass, refine differs and prior persists, accept->reload->few-shot round-trip via scripted playback, vocabulary gap, direct-route 422, double-submit/accept locks, live-region announcements, accessible names, keyboard+focus, axe over pending/failed/success/refinement). Real-model evidence (gemma4:e4b over the scoped demo contract): fresh ask passed S1-S3 first attempt; seeded refinement passed and applied exactly the requested changes (Degraded/ amber/us-east), conversation recorded in the audit report. Exhibit e2e 106 passed/4 skips; hosted composer smoke 13/13; typecheck clean; one dspack-gen version (0.2.0) across the tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR delivers the Phase 3 “Build” vertical-slice experience in the composer: chat-driven surface generation from a project’s approved component vocabulary, streamed gate visibility (S1–S3 + repairs + emit), refinement seeded by the prior surface, and server-side fail-closed acceptance into the contract’s worked-example corpus.
Changes:
- Bump
@aestheticfunction/dspack-gento0.2.0and wire conversation-seeded refinement through/project/run. - Add composer-core “Build” logic (readiness gating + streamed event folding + vocabulary-gap detection) and a new Build UI view with Build-first IA defaults.
- Add agent-side
/project/save-example(fail-closed) plus new E2E + unit coverage for Build flow and accessibility states.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks the workspace to @aestheticfunction/dspack-gen@0.2.0 and adds new workspace deps. |
| playwright.config.ts | Excludes Build specs from the default Playwright config. |
| playwright.composer-agent.config.ts | Includes Build specs in the composer-agent Playwright project. |
| packages/composer-core/src/index.ts | Re-exports Build readiness/folding/gap utilities from composer-core. |
| packages/composer-core/src/composer-core.test.ts | Adds unit tests for Build readiness, streaming fold, and vocabulary-gap classification. |
| packages/composer-core/src/build.ts | Introduces the pure Build domain logic (readiness, fold, vocabularyGap). |
| e2e/support/agent-project.ts | Updates helpers for Build-first navigation and adds a “connectReady” helper. |
| e2e/composer-build.spec.ts | Adds end-to-end Build flow coverage (disabled reasons, streaming, refine, accept, vocabulary gap, fail-closed save). |
| e2e/composer-build-a11y.spec.ts | Adds accessibility tests for live announcements, control names, keyboard flow, and axe scans across states. |
| e2e/composer-agent.spec.ts | Adjusts existing agent E2E tests for Build-first navigation behavior. |
| docs/COMPOSER.md | Documents the Phase 3 Build model, readiness requirements, providers/privacy, and acceptance semantics. |
| apps/web/package.json | Bumps @aestheticfunction/dspack-gen dependency to ^0.2.0. |
| apps/composer/package.json | Bumps @aestheticfunction/dspack-gen and adds @dspack-studio/agui-bridge dependency. |
| apps/composer/app/views/project-view.tsx | Adds Build-first runway text and “Start building” CTA when ready. |
| apps/composer/app/views/build-view.tsx | Adds the Build UI (prompt, model/intent selectors, streamed pipeline, gap panel, accept/refine controls, rendering). |
| apps/composer/app/state.tsx | Adds Build state, readiness computation, streaming run orchestration, and acceptance integration. |
| apps/composer/app/composer.tsx | Adds Build as first nav item, auto-opens Build when ready, and disables Build with exact readiness reason. |
| apps/composer/app/agent-client.ts | Adds model listing, AG-UI streaming client for /project/run, and /project/save-example client call. |
| apps/agent/src/project.ts | Adds conversation parsing + scripted refinement behavior, selects latest example for few-shot/scripted, and implements /project/save-example. |
| apps/agent/src/project.test.ts | Adds tests for /project/run streaming/refinement and /project/save-example fail-closed acceptance/refusals. |
| apps/agent/package.json | Bumps @aestheticfunction/dspack-gen to ^0.2.0 and updates dependency ordering. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+142
to
+145
| export async function connectReady(page: Page, root: string): Promise<void> { | ||
| await connect(page, root); | ||
| await page.getByTestId("nav-build").isEnabled(); | ||
| } |
Comment on lines
+590
to
+603
| const result = await agentSaveExample(projectPath, { | ||
| id: exampleId, | ||
| intent: turn.intent, | ||
| name: `Chat: ${turn.prompt.slice(0, 60)}`, | ||
| prompt: turn.prompt, | ||
| surface: turn.progress.surface, | ||
| }); | ||
| if (!result.ok) { | ||
| setNotice(`Accept failed: ${result.error}`); | ||
| return; | ||
| } | ||
| if (!result.value.ok) { | ||
| setNotice(`Accept refused by the gates: ${result.value.findings.map((f) => f.message).join("; ").slice(0, 300)}`); | ||
| return; |
Comment on lines
+497
to
+502
| const clearBuildThread = useCallback(() => { | ||
| buildStream.current?.cancel(); | ||
| buildStream.current = null; | ||
| setBuildTurns([]); | ||
| setBuildBusy(false); | ||
| }, []); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The ratified Phase 3 vertical slice. Build is now the primary product experience: select a governed project, describe an interface in chat, and get a surface generated from the approved design-system vocabulary — validated in front of you, rendered through the trusted registry, refinable conversationally, and savable as a governed worked example. Consumes published
@aestheticfunction/dspack-gen@0.2.0(dspack-gen#49, released via OIDC before this PR — no override).The interaction flow
Each turn shows: the ask → streamed pipeline (attempt N → S1/S2/S3 per attempt → the verbatim repair message when sent → emit) → outcome → the surface rendered through the project registry → Accept / Refine. Refinement seeds the immediately prior surface + the new instruction (
RunOptions.conversation), regenerates a complete surface through every gate, and keeps prior turns in the thread for comparison and audit. Accept is server-side fail-closed (/project/save-example: S1–S3 re-linted on the agent, unknown intents refused, harness + ledger preserved) and lands the result as a contract worked example — immediately part of that intent's few-shot corpus, so accepted results compound generation. Builds never write intents, rules, rationales, mappings, or casualty declarations.Honest failures: findings verbatim; an ask needing components the owner hasn't approved is named as a vocabulary gap (evidenced structurally from S2, never from message text), recorded in the turn as the future Component Workshop hook — never silently invented.
Build-first IA: Build is the first nav item and the default view for a ready connected project; otherwise visibly disabled with the exact reason (
no intents authored — author one in Governance,2 component(s) unmapped — finish the Mapper, …). The project home checklist reads as the runway and gains Start building when ready.Providers and privacy
scriptedis the always-available zero-model twin: a fresh run plays a contract-derived S2 violation then the intent's latest worked example — every scripted run demonstrates the governed fail→repair→pass loop deterministically, and accepting visibly changes what scripted plays next. A refinement returns the prior surface with a deterministic textual change only when the seed is present (the ratified non-vacuous proof, pinned at three layers). Local Ollama is the first real provider; Anthropic keeps working through the agent's env (no new UI, no claims). No browser keys, no hosted AI, no new bindings. The Build view states the provider and privacy boundary; project source never leaves the machine.Real-model evidence (
ollama:gemma4:e4b, the scoped demo contract)passed, rootinfo-card— the same 8-GB-class model that scored 0/72 on the full shadcn vocabulary.Degraded,amber, andus-east; the two-turn seed is recorded verbatim in the audit report.Evidence
role="status"live announcements, unambiguous accessible names, keyboard + focus (Accept unmount focuses the confirmation), axe over pending/failed/success/refinement states.Out of scope, per ratification
Hosted AI/bindings · persisted multi-thread history · accounts/collaboration · intent inference · Component Workshop (hook only) · React component generation · Vue/Vuetify · per-node visual editing · patch-based refinement · a second surface artifact · grammar-budget UI · dspack-emit#26 · unrelated redesign.
🤖 Generated with Claude Code