How the demo outputs were made
The demo ("Same prompt. Two systems.") shows one representative output per prompt and system. Every output was generated by a model; none was written or edited by hand. This file records exactly how, so anyone can check or repeat it.
Setup
- Model: Claude Opus 5.5 (
claude-opus-5-5), run as Claude Code subagents. - Runs: 3 prompts × 2 systems × 3 runs = 18 runs, on 2026-10-06 between 13:10:29Z and 13:11:45Z.
- Prompts (from
demo/model.ts, identical for both systems):- pricing-card: "Create a pricing card for our Pro plan: €29 per month, billed annually, with four features and a primary call to action to start a free trial. Mark it as the most popular plan."
- settings-form: "Build a notification settings form with an email digest frequency, a product updates toggle, a security alerts toggle that is always on, and a save button."
- dashboard-empty-state: "Design the empty state for the Invoices dashboard when a new user has no invoices yet. Explain what goes here, with a primary action to create the first invoice and a secondary option to import from CSV."
The two systems
Both were frozen before any run, and are published unchanged, with every run, in the public repository.
- Unstructured system (
demo/systems/unstructured, imported as@shared/ui, frozen before any run): a realistic legacy UI library. Value-named tokens (blue-500,gray-300, …; the brand teal is present but not marked as the brand), undocumented components, a duplicate primary button (Buttonand an olderPrimaryButton), numeric spacing props, no empty-state pattern. The rule when writing it: it may lack intent and documentation, never quality. It has real buttons, a labelled toggle, a select and layout helpers. - AI-ready system (
demo/systems/ai-ready, imported as@acme/ui, frozen before any run): semantic tokens (acme.color.action.primary,acme.space.lg, …), components that document when to use them, and patterns such asEmptyState. The last change before the runs only removed header comments that mentioned the demo. Disclosure:EmptyState's copy rule ("sentence case, no exclamation marks or emoji") was added while an earlier hand-written draft of the demo existed (see below).
Isolation
- Every run was a fresh subagent with no memory of other runs.
- Each run got its own folder containing only
TASK.mdand a copy of its own system (system/). It never saw the other system, the site,CLAUDE.md, the annotations, the earlier draft, or any hint of a comparison. Both systems were scanned for words that could leak the purpose ("comparison", "demo", "AI-ready", …) before the runs. - The repo's
CLAUDE.md(which describes the demo) was moved out of the repo for the duration of the runs and restored afterwards. No user-levelCLAUDE.mdor memory existed. - Audit: all 18 transcripts were checked for file paths or commands outside the run's folder: 0 found.
- Limitation: subagents had filesystem tools; isolation was by instruction and folder layout, verified by the audit, not enforced by a sandbox.
The instruction given to each subagent (only the folder path differed per run):
Your working directory for this task is <run folder>. Read TASK.md and every file in system/ in that directory, then write output.tsx in that directory as TASK.md describes. Work only inside that directory: don't read, list or search any file outside it, and don't run commands that touch other paths. When you're done, reply with the single word: done.
TASK.md, identical for both systems except {{ALIAS}} (@shared/ui or @acme/ui) and {{PROMPT}}:
# Task
You're a frontend engineer on a product team. Build the UI described below as a React component.
## Request
{{PROMPT}}
## Your team's UI library
Your team's UI library is in `./system/`. Read it before you start. It's importable as `{{ALIAS}}`.
## File requirements
Write exactly one file: `./output.tsx` (TypeScript + React).
- `export default` one component that takes no props.
- `export const styles` must be a plain CSS string with any styles the component needs (it can be empty). It's injected into a Shadow DOM root that wraps your component; `:host` is that root. The UI library's own styles are already injected.
- No Tailwind, CSS modules or global CSS. Use system font stacks only.
- The component renders in a container 320–600px wide: use `@container` queries, not `@media`.
- No data fetching and no browser APIs during render. Local state is fine. Use static ids, not `useId`.Every run, unedited
demo/runs/<prompt>/<system>/run-N.tsx: byte-identical to what each subagent wrote (checked with cmp).
Choosing the representative run
Rule, written at 2026-10-06T13:11:24Z, while runs were in progress and before any output was opened:
For each prompt × system, the representative run is the run that shares the most structural decisions with the other two runs of the same combination: which library components it uses (or rebuilds by hand); how it styles (library classes/tokens vs. its own CSS values); layout and element structure; accessibility-relevant choices. Not the best-looking and not the worst. Ties go to the lowest run number.
| Prompt | System | Shown | Why |
|---|---|---|---|
| pricing-card | unstructured | run 1 | Runs 1 and 2 are nearly identical (Button blue, blue Tag, hand-built list); run 1 also shares the blue check icon with run 3. Run 3 alone used PrimaryButton. |
| pricing-card | AI-ready | run 1 | All three use Card/Badge/CheckList/Button; run 1 shares the CTA position with run 2 and the header row with run 3. Run 3 alone moved the CTA to the bottom. |
| settings-form | unstructured | run 2 | Shares the tag-in-title and status line with run 1, and the label-above-select field with run 3. |
| settings-form | AI-ready | run 2 | Runs 2 and 3 are structurally identical; run 1 differs only in the footer. Tie → run 2. |
| dashboard-empty-state | unstructured | run 3 | Shares the numbered steps list and hint line with run 1, and PrimaryButton, the 72px icon badge and the centered layout with run 2. |
| dashboard-empty-state | AI-ready | run 1 | All three use EmptyState; run 1 shares the upload icon on the link with run 2, and the page copy and container width with run 3. |
Edits to wire outputs in
- Output files: none.
demo/outputs/<prompt>/<system>.tsxare byte-identical copies of the runs above. They compiled and rendered without changes. demo/registry.ts: the unstructured system'sstylesare now injected into unstructured panels, as@acme/ui's already were for AI-ready ones. This is infrastructure, not output.- Display only: "View code" shows the component before the
stylesexport (displaySourceindemo/load-sources.ts), and the preview pane has padding around each output (DemoPanel). The file content is unchanged. - Run switcher: each panel can show any of its three saved runs (rendered from
demo/runs). The representative run is the default and is labeled "typical"; the comments refer to the typical runs only.
The earlier hand-written draft
Before these runs, the demo showed six outputs written by hand to illustrate typical failures. They were discarded and replaced by the generated runs above. Several of their claims did not survive contact with real output, and the comments were rewritten from the runs only:
- A clickable
divCTA,divtoggles without labels, an "Always on" setting shown as an off toggle, emoji and gradients, "No invoices yet!": none of these appeared in any generated run. The legacy library's real components absorbed most accessibility issues.
What actually differed
Ordered from the strongest difference to the smallest; the demo's tabs follow this order. Every claim below was checked against all three runs, not just the one shown.
- pricing-card:
- Brand: all 3 unstructured runs made the CTA and the "most popular" tag blue (
blue-500/blue-600), never the brand teal; all 3 AI-ready runs usedButton variant="primary"(teal). - Reuse: all 3 unstructured runs hand-built the feature list and drew their own check icon; all 3 AI-ready runs used
CheckList. - Values: the unstructured runs typed numbers (
padding={24},gap={20},36px/40px/48pxprices); the AI-ready runs used scale names, except two values copied from the heading style (font-weight: 650,letter-spacing: -0.02em). - Emphasis: all 3 AI-ready runs used
Card emphasis="highlighted"; only 1 of 3 unstructured runs emphasized the card.
- Brand: all 3 unstructured runs made the CTA and the "most popular" tag blue (
- dashboard-empty-state:
- Pattern: all 3 AI-ready runs used
EmptyState(9–16 lines of their own CSS). - Invented layouts: each unstructured run designed its own layout, with a how-it-works list nobody asked for, in 75–102 lines of CSS.
- Duplicate button: 2 of 3 unstructured runs used the duplicate
PrimaryButton. - Brand: the icon art is blue in all 3 unstructured runs.
- Copy: sentence case on both sides; no emoji anywhere.
- Pattern: all 3 AI-ready runs used
- settings-form: the smallest difference of the three.
- Both are accessible. In both, security alerts is a disabled control with an explanation.
- Doubled labels: all 3 unstructured runs passed a
labelto the legacyToggleand wrote their own row title;Togglerenders its label visibly, so every setting reads twice. - Brand: all 3 unstructured save buttons are blue; the AI-ready ones are teal.
- Spacing: unstructured gaps are typed numbers that mostly match the legacy scale (24/20/4), with
gap={6}and2pxin one run; AI-ready gaps are scale names.
- Consistency: the 3 AI-ready runs of each prompt are near-identical; the unstructured runs vary more (e.g. which primary button, which extra list).
Reproduce
- Clone the public repository. Its
systems/folders are the inputs;runs/holds every output;tasks/holds everyTASK.mdexactly as given. - For each prompt and system, create a folder with
TASK.md(template above) and a copy of that system assystem/. - Give a fresh Claude Opus 5.5 agent the instruction above, three times per folder.