feat: UX clarity — project ownership, Examples separation, honest fallback, always-available Export - #70
Merged
Merged
Conversation
…itch/remove) Ran in the adoption-milestone closeout (8/8 incl. production) but was never committed — the working tree held it since. Landing it verbatim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A catalog name with no native visual now renders as its universal wireframe representation inside the otherwise-native surface — never the raw red "[unimplemented: X]" placeholder in the product canvas. Native renderer when available → wireframe when not; the gap stays visible, not alarming. - registries.ts: registryFor merges wireframe impls under the native custom map (native wins name-by-name), with a per-(catalog,id) identity cache — A2uiCanvas memoizes ingestion on registry identity and the Build canvas calls registryFor in its render body, so identity stability is required, not an optimization. New nativeRegistryFor + wireframeFallbackNames expose the honest PRE-merge gap. - Preview: the coverage caption now reads "N of M components render as wireframe (no native shadcn visual yet) — hover for the list" instead of "unimplemented in this registry"; wireframe mode says what it is. - app/registries.test.ts: 5 invariants (full post-merge coverage, honest pre-merge gap incl. Separator/Checkbox/Textarea, no fallback for full-coverage/wireframe, object identity, native-wins) — fail-first proven against the old registryFor (5/5 fail), green on the new (19/19). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build → Preview → Export without returning to Projects: a quiet Export control sits beside the project context whenever a project is open (same portable file as the hub card; no dropdown machinery). The context chip now states the project's SOURCE in the hub's own words — shadcn/ui / Astryx / Imported / Local repository — instead of conflating source with the HOSTED execution mode (imported projects previously read HOSTED). Every working route without a project now falls back to the hub; scenarios/checks/ component/mapper/repository previously rendered a blank page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The authored delta the project model always promised (projects.ts:12-15),
implemented: a browser project OWNS its accepted surfaces, persisted per
project id and merged over its base vocabulary on open. The canonical
reference is never mutated. Reference contract/profile + project-authored
delta = working project.
- projects.ts: composer.project.examples.<id> delta store (quota-honest),
mergeExamples (replace-by-id else append — authored work lands LAST, which
is what scripted replay and few-shot want), examplesDelta (recomputed
live-vs-base each save; no incremental bookkeeping), nextChatExampleId
(agent's rule, kept in sync); duplicate copies the delta, remove clears it.
- state: loadReference(id, projectId?) merges the delta and records
referenceExampleIds (provenance by BASE MEMBERSHIP, never id prefix);
loadImported merges its delta (an imported bundle is all project-owned);
saveContract's browser branch persists the delta — scenario-view saves get
persistence for free; exportProject merges the delta for closed projects
so "take it with me" includes accepted work.
- Accept works in the browser: acceptBuildTurn's browser branch runs the
SAME deterministic gate client-side (S1–S3 via lintOneSurface, authored
intent, collision-free ex.chat-N against the merged set, never overwrite)
then saves through the one funnel. The agent remains for writing to a real
repository — not a prerequisite for owning browser-authored work. The
Build control reads "Add to project" in both modes with an honest hint.
- Preview: "Your surfaces" (opens on the LATEST project surface) vs
"Reference examples · <DS>" — never one anonymous list; an inspected
reference example says what it is; honest empty state ("none yet — build
something, then Add to project").
- Scenarios: ViewHeader + ratified framing; "Project scenarios" primary with
its own empty state; "Reference examples · <DS>" secondary with
copy-to-project (the canonical row is never edited in place; agent
projects are unaffected — their contract is wholly their own).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mple workspaces
The hub now draws the line the mental model needs: "Your projects" holds only
what the user created or imported; a new "Examples" section holds the
packaged references as teaching material — "Open example" / "Create copy",
never mixed with the user's work.
- Opening an example is EPHEMERAL: no StoredProject, no lastOpened (a reload
lands where you actually work), pristine on every open. The shell labels it
unmistakably — EXAMPLE chip + a banner ("a read-only reference; explore and
edit freely; changes are not kept") with "Duplicate into your projects".
Play is allowed because demo-mode edits were always session-only; honesty
is the label, not a lock.
- duplicateExample carries the session's accepted work into the new project
via the authored delta — "duplicate to keep what you build" is real.
- Example workspaces flip the ownership framing: Preview shows "Example
surfaces" (the reference gallery IS the content), Scenarios "Example
scenarios", Build's accept hint says session-only + duplicate to keep. No
Export in an example (an example is not the user's portable work).
- Examples section renders AFTER Your projects with example-* testids — the
smoke suite's project-export-* prefix locator and grid-scoped selectors are
unaffected; the first-run empty state points to the examples.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tities A user-created browser project is not a demo. "Hosted/in-browser" is an execution mode; "example/reference" is teaching material; "my project" is ownership — the copy now keeps those apart everywhere. - Demo purge: Build's "You're in the hosted demo — no install" → "This project runs in your browser — no install"; every per-view "Demo: … edits stay in memory" note replaced with precise session/persistence language (Scenarios now truthfully says saves persist to the project); the factually-stale runEmit/mapper claims of "build-time emit" corrected (emit recomputes live in-browser). - Stale references: Preview's empty state no longer points at a "Validate view" or a "load the demo" that doesn't exist; Checks stops rendering a permanently-disabled "Emit catalog" button for browser projects and states what actually happens; component-view says Catalog (not Inventory); Build says "Projects → Connect a repository" (not "connect from Project"); notices say Checks (not Validate) and Repository view (not "Ownership panel"). - Page identity: title "Composer · Aesthetic Function" (was "Catalog Composer · Aesthetic Function Studio") + a description that matches the actual product story. - Ratified framings: Catalog = "the governed component vocabulary available to this project" with a summary line (N components from <DS> · M mapped · K declared casualties, casualties explained); Governance = "the design-system rules and intent context governing this project" with a summary line and intent NAMES first (ids secondary) instead of a wall of id+prose; Scenarios' ViewHeader landed with the ownership split. - Dead code: DEMO_* consts, loadDemo, and Reference.emit removed — the two never-read ~380 KB emit JSONs leave the client bundle; stale comments corrected (modes doc no longer claims an "Acme UI demo project"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three new smoke specs prove the UX-clarity milestone end to end, fail-first
verified against a build of pre-milestone main (all three fail there: no
browser Accept, no Preview ownership groups, no Examples section):
- a built surface becomes first-class project content: scripted build →
"Add to project" → ex.chat-1 is Preview's DEFAULT surface with the
reference corpus in its own labeled group → Scenarios lists it under
Project scenarios → survives reload (persisted delta) → top-bar Export's
file carries it. Also asserts the honest wireframe fallback: a reference
surface using Switch/Separator renders [data-wireframe] stand-ins under
the shadcn registry, the caption says "render as wireframe", and raw
"[unimplemented:" text never appears.
- a fresh project's Preview is honest: "none yet — build something", the
reference gallery labeled as references, and an inspected reference
example states it is teaching material.
- Examples are teaching material: separate hub section, ephemeral read-only
workspace (EXAMPLE chip + banner; never in Your projects; a reload lands
the hub, not a resurrected example), and Duplicate creates a normal
project.
Also: composer-build-a11y updated for the accept control's new accessible
name ("Accept turn N into the project" — the intended rename).
Local gates: smoke 11/11 · composer units 19/19 · repo typecheck clean ·
agent suite green across runs (one intermittent in #69's provider test,
4/4 alone — filed separately, not chased here).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
dspack-studio-composer | f20bde4 | Aug 10 2026, 04:04 PM |
… does registries.test.ts asserts against the real generated emit catalogs; CI's unit step ran before anything generated them (locally they existed from typecheck). Mirror the typecheck script's own idiom. Verified by deleting app/demo/generated and running the test script: regenerates, 19/19. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR delivers the “UX clarity” milestone for Composer by making project ownership explicit across views, separating packaged Examples from user projects, persisting browser-authored worked surfaces as first-class project content, and ensuring Preview never shows raw unimplemented placeholders (wireframe fallback is always used).
Changes:
- Add a persisted “authored examples delta” for browser projects (accepted Build surfaces + authored Scenarios), merged over reference/imported bases on open/export.
- Introduce an Examples hub section with an ephemeral read-only “example workspace” plus “Duplicate into your projects” to keep session-accepted work.
- Make Preview’s native registries compose a per-component wireframe fallback, add honest coverage messaging, and add always-available in-context Export.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| e2e/composer-prod-smoke.spec.ts | Adds prod smoke coverage for browser accept→persist→preview/scenarios separation, examples hub behavior, and in-context export. |
| e2e/composer-build-a11y.spec.ts | Updates accessible-name assertion for the renamed Accept/Add-to-project control. |
| apps/composer/app/views/validate-view.tsx | Makes Emit control honest/conditional and updates explanatory copy for browser vs agent mode. |
| apps/composer/app/views/scenario-view.tsx | Splits project scenarios vs reference examples; adds “copy to project” for references; updates framing/header. |
| apps/composer/app/views/projects-view.tsx | Adds Examples section (open ephemeral example / create copy) and updates empty-state guidance. |
| apps/composer/app/views/preview-view.tsx | Separates “Your surfaces” vs reference surfaces; defaults to latest project surface; replaces coverage logic with wireframe-fallback reporting; adds reference labeling and empty-state. |
| apps/composer/app/views/mapper-view.tsx | Updates persistence language to reflect browser-session vs agent-backed saves. |
| apps/composer/app/views/inventory-view.tsx | Reframes Catalog copy and adds a concise catalog summary line. |
| apps/composer/app/views/governance-view.tsx | Reframes Governance copy, adds summary counts, and changes intent list presentation. |
| apps/composer/app/views/component-view.tsx | Updates terminology (“Catalog”) and save-status message for browser session behavior. |
| apps/composer/app/views/build-view.tsx | Makes “Add to project” available in-browser (and in example sessions with correct messaging); updates hosted/browser copy. |
| apps/composer/app/state.tsx | Implements examples-delta persistence/merge, example workspace + duplication, browser-side accept gating, updated notices, and removes demo-loading path. |
| apps/composer/app/registries.ts | Adds merged registry composition (native + wireframe fallback), identity caching, and fallback-name reporting. |
| apps/composer/app/registries.test.ts | Adds unit tests for merged registry coverage, honest gap reporting, identity caching, and precedence rules. |
| apps/composer/app/projects.ts | Adds localStorage-backed examples-delta storage/merge/diff utilities and ensures duplicate/remove carry/clear the delta. |
| apps/composer/app/layout.tsx | Updates product title/description metadata to match new framing. |
| apps/composer/app/hosted-build.ts | Updates error copy to reflect “browser project” terminology. |
| apps/composer/app/demo-data.ts | Reframes packaged references as design systems/examples material; removes pre-emitted JSON from reference shape. |
| apps/composer/app/composer.tsx | Updates project-context chip semantics, adds top-bar Export (non-example), adds example banner + duplicate action, and makes hub fallback cover all working routes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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 UX-clarity milestone: no new technology — the existing capabilities made understandable, and every screen made to reflect the active project. Every screen now answers What project am I looking at? What is this page for? What should I do next? Root causes from the approved audit; design forks implemented per the owner's decisions.
The one authorized behavior change — user-built surfaces are first-class project content (D2)
The architecture's own promise (projects.ts: reference-backed projects "may persist a small authored delta") — implemented:
composer.project.examples.<id>) holds accepted builds and authored scenarios; it merges over the base on open. The canonical shadcn/Astryx reference is never mutated.ex.chat-N, never overwrite. The agent remains for writing to a real repository — not a prerequisite for owning browser-authored work.Examples separated from Your projects (D1)
A new hub Examples section (after Your projects): each packaged reference as a read-only example — Open example / Create copy. Opening is an ephemeral workspace: no StoredProject, no lastOpened (a reload lands where you actually work), pristine every open, labeled by an EXAMPLE chip + banner ("explore and edit freely; changes are not kept") with Duplicate into your projects — which carries session-accepted work via the delta. Reference examples remain inside user projects as internal context (few-shot, scripted replay, governance evidence) but never masquerade as authored content.
Honest wireframe fallback (D3)
registryFornow composes the universal wireframe visual under each native registry, per component (native wins name-by-name; identity-cached because the canvas memoizes on registry identity). Raw[unimplemented: X]text never appears in the product canvas; Preview's caption reads "N of M components render as wireframe (no native shadcn visual yet) — hover for the list". No renderer campaign; nothing silently approximated.Export from the working experience (D4)
A quiet Export beside the project chip — Build → Preview → Export without returning to Projects. Hub card Export kept; examples don't export (duplicate first).
Clarity fixes (F1–F7)
demo-project/untouched (Acme migration golden).Verification
Test plan
🤖 Generated with Claude Code