@fugood/bricks-ctor 2.25.0-beta.11 → 2.25.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/compile/__tests__/util.test.js +278 -0
  2. package/compile/index.ts +2 -0
  3. package/package.json +3 -3
  4. package/skills/bricks-design/SKILL.md +172 -45
  5. package/skills/bricks-design/references/architecture-truths.md +125 -0
  6. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  7. package/skills/bricks-design/references/design-critique.md +195 -0
  8. package/skills/bricks-design/references/design-languages.md +265 -0
  9. package/skills/bricks-design/references/performance.md +116 -0
  10. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  11. package/skills/bricks-design/references/translating-inputs.md +152 -0
  12. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  13. package/skills/bricks-design/references/verification-toolchain.md +213 -0
  14. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  15. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  16. package/skills/bricks-design/references/workflow.md +134 -0
  17. package/skills/bricks-ux/SKILL.md +120 -0
  18. package/skills/bricks-ux/references/accessibility.md +162 -0
  19. package/skills/bricks-ux/references/flow-states.md +175 -0
  20. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  21. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  22. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  23. package/skills/bricks-ux/references/user-journey.md +168 -0
  24. package/skills/bricks-ux/references/ux-critique.md +256 -0
  25. package/tools/deploy.ts +4 -0
  26. package/tools/pull.ts +42 -2
  27. package/types/automation.ts +1 -0
  28. package/types/data-calc.ts +1 -0
  29. package/skills/bricks-design/LICENSE.txt +0 -180
@@ -0,0 +1,152 @@
1
+ # Translating Inputs
2
+
3
+ When the user arrives with a Figma render, an HTML mockup, a screenshot, a competitor URL, or a brand book, you are translating across an impedance mismatch. Web/Figma artifacts encode assumptions (scroll, hover, modal, semantic links, responsive reflow) that BRICKS does not share. Translate intent, not pixels.
4
+
5
+ ## Step 0 — visual inspection is mandatory and exhaustive
6
+
7
+ You cannot translate from material you have not actually seen. Markdown extractions, text summaries, and `llms.txt`-style indices preserve words and lose every visual signal that makes the reference design-relevant — hierarchy, rhythm, photography style, motion language, brand temperature, typography pairing, density, color weighting. Translating from those alone produces designs that miss the point.
8
+
9
+ Hard rule before any translation work begins:
10
+
11
+ - **List every reference artifact** the user supplied (URLs, Figma frames, HTML files, PDFs, screenshots, videos).
12
+ - **Visually inspect each** to completeness — every page, every frame, every state.
13
+ - **State the coverage out loud** in your reply ("inspected: home page, product page, 3 case-study pages, 12 Figma frames, all 8 PDF pages") so the user can spot gaps.
14
+ - If the host environment lacks the tooling to actually see something (no browser automation, no PDF rendering), **ask the user for screenshots before continuing**. Do not pretend.
15
+
16
+ How to inspect, by source type:
17
+
18
+ | Source | How to actually see it |
19
+ |---|---|
20
+ | Public URL (website / docs / product page) | Drive a browser automation tool — browser-MCP, Playwright/Puppeteer-style MCP, an `agent-browser`-equivalent skill, or any host-provided browser tool — to navigate the page and capture full-page screenshots. Then read each screenshot back through the host's image-reading capability. `WebFetch` and similar text extractors are **not** visual inspection; use them only as navigation aids to enumerate which pages to visit. |
21
+ | Figma / design-tool URL | Use a Figma-MCP / design-tool MCP if present to enumerate frames and image-export each. Otherwise ask the user for PNG exports of every relevant frame. |
22
+ | HTML project on disk | Serve and screenshot every route via a browser tool, the same as a public URL. Do not infer visuals by reading source HTML/CSS — the rendered output is what you need. |
23
+ | PDF / brand book / slide deck | Read every page through the host's PDF capability, page-by-page when the document is long. Cover all pages, not just the first few. |
24
+ | Local images / screenshots | Read each via the image tool. |
25
+ | Video walkthrough | Ask the user for key-frame screenshots, or to describe each state. Do not claim to have "watched" a video you cannot frame-step. |
26
+ | Live competitor product / app | Run it (or have the user run it and capture screen recordings); inspect the screenshots/states. |
27
+
28
+ If a reference is partially inaccessible (paywalled page, unauthenticated content, broken link), name the gap explicitly. Don't extrapolate from the visible portion.
29
+
30
+ Inspection comes before everything else in this file. The translation table below assumes you have already seen every relevant frame.
31
+
32
+ ## A render is one state, not an Application
33
+
34
+ Most translation mistakes start here. A Figma frame, a screenshot, an HTML page — each is a snapshot of one state. It is never an Application.
35
+
36
+ Before translating, identify:
37
+
38
+ - **Which Canvas is this?** Welcome / browsing / editing / confirming / receipt / error?
39
+ - **What Data drives the visible variation in this state?** Which strings, images, lists are dynamic vs. static?
40
+ - **What other Canvases does this flow imply?** A "submit" button implies at minimum a result/confirmation Canvas. A list view implies a detail Canvas (or a Subspace overlay).
41
+ - **What shared chrome carries across?** Logo, header, status bar — these become same-id Bricks across multiple Canvases for auto-tween.
42
+
43
+ Translating a single render verbatim into one Canvas with no state graph is the most common mistake when porting from web mocks. Don't.
44
+
45
+ ## Translation table — web/Figma → BRICKS
46
+
47
+ ### Scroll
48
+
49
+ Web overflow scrolling has no BRICKS equivalent. Pick one:
50
+
51
+ - **Many items, lightweight, glanceable** → `Slideshow` Brick on a timed loop.
52
+ - **Many items, user-driven browse** → `Items` Brick (virtualized list with templated row).
53
+ - **Distinct content sections that don't fit one Canvas** → paginate across Canvases with shared chrome and a paging Brick.
54
+ - **Long form** → Canvas-per-question (Truth #10).
55
+
56
+ If the source design relies on the user scrolling for hours of content, the information architecture needs a redesign before translation. Don't fake it.
57
+
58
+ ### Hover / focus / `:hover` styles
59
+
60
+ - **Touch hardware** → hover doesn't exist; the design needs a touch target instead. Move the affordance to the resting state.
61
+ - **No-touch hardware (signage)** → hover doesn't exist; either drop the affordance or convert to a Switch on a non-pointer Data signal (e.g., a peripheral sensor, a state Data toggled by a different Generator).
62
+ - **Web focus rings** → focusable Brick prop where the brick template supports it (TextInput, etc.); for non-input bricks, drop or use Switch-driven outline.
63
+
64
+ Carrying hover affordances into BRICKS is one of the loudest tells of a sloppy port.
65
+
66
+ ### CSS animations and transitions
67
+
68
+ CSS keyframes are imperative; BRICKS animations are property-targeted (`transform.translateX | translateY | scale | scaleX | scaleY | rotate | rotateX | rotateY | opacity`) with timing / spring / decay configs.
69
+
70
+ Translate **intent**, not keyframes:
71
+
72
+ - A CSS fade-in → a Standby Transition with `standbyOpacity: 0` and a delay.
73
+ - A slide-in from the right → Standby with `standbyMode: 'right'` (or custom standbyFrame) and an x-axis easing.
74
+ - A bounce → an `AnimationSpringConfig` with chosen friction/tension.
75
+ - A continuous CSS pulse → an Animation with `runType: 'loop'` (use sparingly — see performance).
76
+ - A page transition → shared-Brick auto-tween + entrance/exit Standby on non-shared elements (Truth #3 + #7).
77
+
78
+ ### Modal / dialog / popup
79
+
80
+ Web modals are usually overlays painted via z-index. In BRICKS:
81
+
82
+ - **Always-on modal pattern (e.g., language picker that any Canvas can summon)** → Subspace with `portal: 'top' | 'left' | 'bottom' | 'right'` invoked from the host.
83
+ - **Single-use confirmation in one flow** → a Canvas of its own. The design is a state; treat it as one.
84
+ - **Inline reveal (e.g., expandable card)** → Switch-driven Brick visibility on the same Canvas. Don't reach for a Subspace for inline behavior.
85
+
86
+ ### Buttons, links, ARIA roles
87
+
88
+ Translation table:
89
+
90
+ | Source | BRICKS |
91
+ |---|---|
92
+ | `<button>` (text only) | `BrickText` with `on_press` |
93
+ | `<button>` (with bg, border, padding, icon) | `BrickRect` with `on_press`; children `pressable: 'bypass'` |
94
+ | `<a href="...">` | Same as `<button>` — there is no link primitive. `on_press` triggers `CHANGE_CANVAS` (internal) or a Generator (external) |
95
+ | `<div role="button">` | `BrickRect` with `on_press`; children bypass |
96
+ | `aria-pressed` toggle | A toggle Data + Switch on the visual state |
97
+ | `:hover` | Drop on no-touch; convert to a touch state on touch HW |
98
+ | `:focus` | Focusable Brick prop where supported |
99
+ | `:disabled` | Switch on a `disabled` Data; alter visual + set `pressable: 'disabled'` |
100
+
101
+ See [`pressable-composition.md`](pressable-composition.md) for the failure modes around composite buttons.
102
+
103
+ ### Form fields / inputs
104
+
105
+ - Single text input → `BrickTextInput` bound to a Data via DataLink.
106
+ - Long form → Canvas per question (Truth #10), not a scrolling form.
107
+ - Validation → DataCalc producing a `<field>.valid` Data; Switch on the field's appearance to surface error state.
108
+ - Submit → event chain that runs the submission Generator (with idempotency key from Data) and CHANGE_CANVAS to a result Canvas; never a synchronous wait.
109
+
110
+ ### Lists / grids / repeating cards
111
+
112
+ - Static list (≤ 5 items, never changes) → hand-laid Bricks.
113
+ - Dynamic list, glanceable → `Slideshow` (timed) or `Items` (interactive).
114
+ - Long, browse-heavy → `Items` Brick with templated row Brick; bind `data` to a Data array.
115
+
116
+ ### Images, fonts, video, brand binaries
117
+
118
+ Lift to **Media Flow** at translation time. Do not embed base64 in Brick props or paste binaries into the Subspace file.
119
+
120
+ - Images → Data of `kind: { type: 'media-resource-image' }`, referenced by Bricks via DataLink.
121
+ - Fonts → `ApplicationFont` entries in the Application; declare in `applicationSettings`.
122
+ - Video / Lottie / Rive → corresponding `media-resource-video` / `lottie-file-uri` / `rive-file-uri` Data with preload metadata.
123
+
124
+ When the source contains brand binaries, you are inheriting them — but only at the resolution and quality the source happened to embed, which is often insufficient for the deployment. **Hand the brand-binary work off to the asset protocol** in [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md) rather than translating the visual reference directly into the Subspace as a redraw:
125
+
126
+ - A logo you can see in the Figma mockup is not the logo you ship — go acquire the official high-resolution source through Step 2/3 of the asset protocol.
127
+ - A product photo embedded in the HTML mockup is the *placeholder* the source designer used; verify it's the brand-current canonical asset before binding, and source a higher-resolution version if the embedded one is below the 5-10-2-8 bar.
128
+ - A redrawn-in-Figma logo or product silhouette in the source design is a tell that the source designer didn't have the real asset either — start the asset hunt from scratch, don't carry the redraw across.
129
+ - Embedded brand fonts may not be license-cleared for embedded display use; verify the license and switch to a documented alternative if the constraint isn't met.
130
+
131
+ Score everything against 5-10-2-8 before binding. If the source's brand binaries are unclear, low-resolution, or stylized substitutes, surface as a gap in `brand-spec.md` and either acquire properly or — for non-logo categories — escalate to brand-reference-anchored generation per [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md). **Don't ship Sketch-Brick imitations.**
132
+
133
+ ### Brand colors and tokens
134
+
135
+ - A few colors used in many places → a small set of Data entries acting as theme tokens, bound via DataLink. Worth the indirection.
136
+ - One-off decorative color → hardcode (Truth #2's loose convention).
137
+ - Token system from a brand book (light / dark / accent / semantic) → expose as Data tokens; switch theme by writing to the token Data values.
138
+
139
+ ### Long copy / paragraphs
140
+
141
+ - A signage screen at 5 m has time for one claim. If the source design has paragraphs of body copy, the design is wrong for the deployment, not the runtime.
142
+ - Cut copy until the screen reads in under 2 seconds.
143
+ - For genuinely long copy (terms of service, instructions), Canvas-per-section + a paging Brick beats fake scrolling.
144
+
145
+ ## Order of operations for a porting task
146
+
147
+ 1. **Verify deployment context** (Priority #0 in SKILL.md). Don't translate before knowing the screen size and orientation; you'll waste the translation.
148
+ 2. **Map the source to a Canvas graph.** Sketch which states exist; identify shared chrome.
149
+ 3. **Lift binaries to Media Flow.** Don't translate with embedded data; it pollutes the Subspace.
150
+ 4. **Walk per-Canvas.** For each Canvas, translate elements via the table above. Make explicit pressable decisions for every interactive element.
151
+ 5. **Wire the state machine.** Event chains for transitions; shared-Brick ids for chrome continuity; Standby for entrances.
152
+ 6. **Verify against the actual deployment** (see [`verification-toolchain.md`](verification-toolchain.md)). The translation is done when it runs on the target hardware, not when it looks right in the Electron preview.
@@ -0,0 +1,124 @@
1
+ # Variations and tweaks
2
+
3
+ Two different concerns that look similar from a distance and ruin each other when conflated.
4
+
5
+ - **Exploration variations** — agent offers the user 2–3 directions to compare during the design phase, picks one, the others are discarded. Lives outside the deployed artifact.
6
+ - **Production tweakability** — knobs the operator or environment will actually change once the Application is deployed (locale, theme accent for franchise variants, content feed URL). Lives in Data, with discipline.
7
+
8
+ Keep them separate. The deployed Application is not a palette picker. The exploration phase is not a Data-knob proliferation event.
9
+
10
+ ## Why no on-Canvas variation picker
11
+
12
+ Reference design skills built for unbounded HTML put palette pickers and tweak panels in the artifact corner — the user is at a desktop browser, the panel is welcome. BRICKS deliverables run on target hardware (kiosk, signage, lobby screen). An in-Canvas palette picker is design contamination — a tweak UI bolted into the design, same category as decorative page counters or progress chrome.
13
+
14
+ The comparison surface for exploration variations is **chat and source tree**, never the Canvas.
15
+
16
+ ---
17
+
18
+ ## Exploration variations — three patterns, token-cost order
19
+
20
+ Default to **Pattern 1**. Escalate only when the variation can't be expressed by the cheaper pattern.
21
+
22
+ ### Pattern 1 — Data-preset variations (default, cheapest)
23
+
24
+ Use when variations differ only on values: palette, copy, asset paths, density factor, type tokens, brand text, motion timing constants.
25
+
26
+ **Shape:**
27
+
28
+ - One Canvas / Subspace. All variant-specific values are Data with initial constants.
29
+ - Source has a `VARIATION_PRESETS` map near the top of the Subspace define file. Each preset is a flat map of token values. Keep the map flat — nested config trees make per-preset diffs noisy.
30
+ - Active preset is selected by a single line near the top: `const ACTIVE = PRESETS.bauhaus`. Agent flips this line, runs preview, captures screenshot, repeats.
31
+ - All hero Bricks keep **the same id** across all presets — preserves Shared Brick auto-tween if the user later wants to A/B them at runtime in a settings Canvas (different use case, but the door stays open).
32
+
33
+ **Comparison surface:** screenshot grid in chat reply, captioned with preset name + key choices.
34
+
35
+ **Token cost:** 1 Canvas tree + ~10 lines per preset. A 3-variation comparison ≈ `1 × Canvas + 30 × preset lines`, not `3 × Canvas`.
36
+
37
+ **Example skeleton:**
38
+
39
+ ```ts
40
+ const PRESETS = {
41
+ bauhaus: { ground: '#FFFFFF', ink: '#1A1A1A', accent: '#FF3C00', font: 'Inter', density: 1.0 },
42
+ hara: { ground: '#F8F4ED', ink: '#2A2A28', accent: undefined, font: 'Noto Serif', density: 1.4 },
43
+ brutal: { ground: '#000000', ink: '#00FF00', accent: undefined, font: 'IBM Plex Mono', density: 0.8 },
44
+ }
45
+ const ACTIVE = PRESETS.bauhaus // ← agent flips this one line per variation
46
+ ```
47
+
48
+ Bricks read from `ACTIVE.ground` / `ACTIVE.ink` etc. via Data. The Canvas tree is authored once.
49
+
50
+ ### Pattern 2 — Shared module + variant Subspaces (medium)
51
+
52
+ Use when variations share most of the Brick tree but differ on a chunk that can't be expressed as Data — different motion language, different hero element identity, different Canvas-graph rhythm, different Standby Transition feel.
53
+
54
+ **Shape:**
55
+
56
+ - A shared module (a Subspace-as-typed-module per Truth #8) exports the common Brick definitions, hero ids, and Data shape.
57
+ - Each variant Subspace imports the shared module and overrides only the variant-specific chunk.
58
+ - Bricks reused by reference, not duplicated.
59
+
60
+ **Comparison surface:** screenshot per variant via preview MCP, assembled in chat.
61
+
62
+ **Token cost:** 1 shared module + N small variant Subspaces.
63
+
64
+ **Caveat:** if the project's Subspace composition support is limited (some setups don't allow clean cross-Subspace imports of partial Brick trees), escalate to Pattern 1 with a Data-driven Switch on the variant-distinguishing chunk before falling all the way to Pattern 3. A few Switches and a single extra Data value can absorb a surprising amount of "structural" difference.
65
+
66
+ ### Pattern 3 — Separate Subspaces (heaviest, justified case only)
67
+
68
+ Use when variations differ structurally — Canvas-graph shape (Slideshow vs state-machine vs hybrid), interaction archetype, fundamentally different Brick tree, fundamentally different Data shape.
69
+
70
+ **Shape:** one Subspace per variation, each stands alone. Previewed individually via the project's Subspace switcher.
71
+
72
+ **Comparison surface:** chat with screenshots from each Subspace.
73
+
74
+ **Token cost:** full Subspace × N. Justified only because the variations *aren't* variants of one design — they're different designs.
75
+
76
+ ### Decision rule
77
+
78
+ ```
79
+ Differs only in token values? → Pattern 1 (Data presets)
80
+ Differs in some Brick subtree but
81
+ shares most + same Canvas-graph shape? → Pattern 2 (shared module)
82
+ Differs structurally / different shape or archetype? → Pattern 3 (separate Subspaces)
83
+ ```
84
+
85
+ Default escalation: Pattern 1 first. Move up only when the cheaper pattern would lie about what's varying.
86
+
87
+ ## Token-saving discipline
88
+
89
+ - **Hero ids identical across presets** — same Brick tree, only Data values differ. Token diff is small; auto-tween stays available for any later A/B Canvas.
90
+ - **Flat preset maps** — each preset is a single object literal, all keys at one depth. Easier to diff, cheaper to edit per variation.
91
+ - **Don't author 3 showcase Canvases inside one Subspace** to compare — triplicates Brick trees for no gain over Pattern 1 + screenshot grid. The temporary-showcase Subspace pattern survives only as a fallback when the user wants to physically walk between variations on real hardware, on explicit request.
92
+ - **Don't escalate proactively** — agent's instinct will be to "just build it three times so the user sees three real things." That's the expensive path. Pattern 1 + a screenshot grid is the same review experience at a fraction of the cost.
93
+
94
+ ---
95
+
96
+ ## Production tweakability — the knob filter
97
+
98
+ After variations are picked and the production Subspace exists, the question is what should stay user-tweakable in deployment. A Data knob has cost — Generator wiring, history bound, persistence behaviour to reason about, every consumer needs an event handler. The bar is **concrete scenario**, not "maybe someone wants to change this."
99
+
100
+ **Passes the filter (becomes a Data knob):**
101
+
102
+ - **Operator-controlled in deployment** — locale switch on a multilingual kiosk, theme accent for franchise variant, content feed URL for menu boards, store hours, item availability.
103
+ - **Environment-driven** — sensor / time-of-day / network state already arriving via a Generator.
104
+ - **Per-instance branding** in fleet deployments where one Subspace serves N venues.
105
+ - **i18n strings** — visible text in a Subspace bound for multilingual deployment, even when only one language is populated at first.
106
+
107
+ **Fails the filter (hardcode it):**
108
+
109
+ - Stuff the agent explored during design but the operator won't touch.
110
+ - "Maybe in the future." Three hardcoded lines beat one Data wire until the future arrives.
111
+ - Tweaks meant for the designer's own iteration loop — that's what source edits and Pattern 1 are for, not Data.
112
+ - Speculative theming. If there's no franchise / no white-label requirement, don't make the palette a Data knob.
113
+
114
+ When in doubt, hardcode. Adding a Data knob later is a small refactor; removing one once consumers exist is a big one.
115
+
116
+ ## Anti-patterns
117
+
118
+ - **In-Canvas palette pickers / colour swatches / "choose your theme" buttons as a deliverable feature.** Same category as page counters drawn into Canvas content. Allowed only as a literal settings Canvas in a settings Subspace, which is a different use case entirely.
119
+ - **Demo-mode toggles left in production code.** Strip them before declaring done.
120
+ - **Three showcase Canvases inside the production Subspace just for variation review.** Triplicates Brick trees. Use Pattern 1 + screenshot grid.
121
+ - **Speculative Data knobs.** Every Data knob without a named operator scenario is dead weight that complicates the next change.
122
+ - **Pattern-3-by-default** because "the user wants to see real options." User wants to see differences, not duplicated effort. Show differences via Pattern 1.
123
+ - **Triplicated Canvas trees + variant-specific Subspaces for what's actually a palette difference.** Always check: could this be a flat preset?
124
+ - **Forgetting to delete the losing presets after pick.** The production Subspace ships with one preset, hardcoded. The `VARIATION_PRESETS` map is exploration scaffolding, not a runtime selector.
@@ -0,0 +1,213 @@
1
+ # Verification Toolchain
2
+
3
+ Three runtime targets, one decision rule. Verify against the cheapest path that proves what you need to prove.
4
+
5
+ ## Definition of done — the hard gate
6
+
7
+ Before the agent is allowed to claim a design is complete, **all** of the following must be true:
8
+
9
+ 1. **Compile clean.** Latest source passes `compile` with no errors.
10
+ 2. **Every Canvas screenshot captured.** A rendered screenshot of every Canvas in the deliverable, captured via `preview` MCP (`responseImage: true`) at the target hardware resolution and orientation. Canvases gated behind events are reached via Automation runs (`testId` / `testTitleLike` with `brick_press` / `wait_until_canvas_change` cases). Single-Canvas Subspaces still require a captured screenshot.
11
+ 3. **Every screenshot reviewed by the agent.** The agent must read each captured screenshot back through the host's image-reading capability — saving the file is not the same as seeing it. The model that designed the Canvas must also have seen the rendered result, or the verification gate has not passed.
12
+ 4. **Reference comparison report.** If the user supplied any reference material (Figma / website / HTML / screenshot / PDF / brand book / competitor), produce a short delta report per Canvas:
13
+ - What matches the reference.
14
+ - What intentionally diverges, and why (deployment constraint, BRICKS-runtime semantic, system commitment).
15
+ - What unintentionally diverges, and the planned fix.
16
+ 5. **Path appropriate to deployment executed.** Path 1 by default. Path 2 (on-device) when the deployment depends on real hardware behaviour, or whenever the brief touches payment, identity, peripherals, or LocalSync.
17
+ 6. **Console clean.** No 404s on media, no Data-key-undefined warnings, no Generator init failures, no React-style warnings — every console line is either zero or accept-with-a-comment.
18
+
19
+ What does **not** count as done:
20
+
21
+ - A single hero-Canvas screenshot.
22
+ - "Looks roughly like the reference" with no per-Canvas comparison.
23
+ - "Compiled successfully" with no rendered preview.
24
+ - A claim of done with no screenshots in evidence.
25
+ - Captured screenshots saved to disk but never read back by the agent.
26
+
27
+ If any item is unmet, the work is mid-iteration. Say so explicitly to the user; offer a precise list of what remains.
28
+
29
+ ## Path 1 — Electron preview (no device required)
30
+
31
+ The default loop. Always available; deterministic; no device wear; safe for side-effecting flows because nothing real fires.
32
+
33
+ ### `bricks-ctor` MCP — `compile`
34
+
35
+ Typecheck + compile the project. Gate every iteration on this; everything below assumes a clean compile.
36
+
37
+ Agent invocation: call the MCP tool `compile` exposed by the `bricks-ctor` MCP server registered for the project. No arguments.
38
+
39
+ ### `bricks-ctor` MCP — `preview`
40
+
41
+ Launches headless Electron, takes a screenshot, optionally runs a named Automation test by id or partial title.
42
+
43
+ Arguments:
44
+ - `delay` (ms before screenshot) — default 3000. Increase if Standby Transitions are still in flight.
45
+ - `width`, `height` — screenshot dimensions in px.
46
+ - `responseImage: true` — return base64 jpeg as MCP image content (recommended for visual sanity).
47
+ - `testId` or `testTitleLike` — run a project Automation before the screenshot. Use `testTitleLike` for fuzzy matches (case-insensitive partial title).
48
+
49
+ Returns text log + (if `responseImage`) base64 image content.
50
+
51
+ ### `bun preview` (project script)
52
+
53
+ Sustained dev session — watches `subspaces/`, recompiles on save, exposes CDP at `localhost:19852` (configurable via `--cdp-port`), writes `.bricks/devtools.json` with port/pid for downstream tooling.
54
+
55
+ Useful flags:
56
+ - `--screenshot` + `--screenshot-delay/width/height/path` — drive screenshot capture without keeping the window open.
57
+ - `--show-menu` — surface the Electron menu (debugging).
58
+ - `--test-id` / `--test-title-like` — run an Automation in this preview.
59
+ - `--no-keep-open` — exit after one screenshot.
60
+ - `--no-cdp` — disable CDP server (rare; default is to expose it).
61
+ - `--clear-cache` — reset cached state between runs.
62
+
63
+ ### `bricks-cli devtools` against `localhost:19852`
64
+
65
+ Same CDP commands as the device path; just point them at the local preview.
66
+
67
+ ```
68
+ bricks devtools screenshot -a localhost -p 19852 -o local.png
69
+ bricks devtools brick tree -a localhost -p 19852 -d 5
70
+ bricks devtools input tap 480 270 -a localhost -p 19852
71
+ bricks devtools storage data-bank list -a localhost -p 19852
72
+ bricks devtools runtime eval -a localhost -p 19852 "document.title"
73
+ ```
74
+
75
+ ### Project Automations
76
+
77
+ E2E tests authored in TypeScript inside the project (`AutomationTest` / `TestCase`). Test cases include:
78
+
79
+ - `brick_press` — synthesize a press on a Brick.
80
+ - `wait_until_canvas_change` — assert navigation.
81
+ - `assert_property` — assert a Data value equals expected.
82
+ - `wait_property_update` — wait for a Data value to change.
83
+ - `match_screenshot` — visual regression with stored reference image.
84
+
85
+ Trigger types: `launch` (runs on app start), `anytime` (manual), `cron` (scheduled).
86
+
87
+ Important: the automation map id must be `'AUTOMATION_MAP_DEFAULT'` (not a generated id) — the preview test runner reads from `automationMap['AUTOMATION_MAP_DEFAULT']?.map`.
88
+
89
+ Run a single test from the agent: `bricks-ctor` MCP `preview` with `testId` or `testTitleLike`.
90
+
91
+ ## Path 2 — Real device with DevTools enabled
92
+
93
+ Required when the deployment depends on hardware behaviour the Electron preview cannot reproduce: physical orientation/DPI, real touch hardware (IR overlays, multi-touch), peripherals (camera, BLE, MQTT, NFC, payment, printer, sensors), watchdog cycles on the actual launcher build, fleet-managed reboot semantics, LocalSync across multiple devices, the actual color/luminance of the panel.
94
+
95
+ **Always Path 2** when the brief touches: payment, identity capture, real-time peripheral data, multi-device LocalSync, certified hardware.
96
+
97
+ ### One-time manual setup (the agent cannot do this)
98
+
99
+ Ask the user to:
100
+
101
+ 1. On the device, open **Settings** → advanced settings.
102
+ 2. Toggle **Chrome DevTools** on. The device starts a DevTools server on the local network on port `19851` (auto-increments if taken).
103
+ 3. Confirm **Enable LAN Discovery** is on (default) so `bricks devtools scan` can find it.
104
+ 4. Note the device passcode if displayed.
105
+
106
+ Requires BRICKS Foundation **≥ 2.24** for CDP commands.
107
+
108
+ ### Endpoints exposed once enabled
109
+
110
+ | Endpoint | URL shape | Use |
111
+ |---|---|---|
112
+ | Web UI | `http://<ip>:19851` | DevTools landing in a browser |
113
+ | CDP | `devtools://devtools/bundled/inspector.html?ws=<ip>:19851/ws/<passcode>` | Chrome DevTools front-end (or `chrome://inspect`) |
114
+ | MCP | `http://<ip>:19851/mcp` | Bridge to Claude Code via mcporter (below) |
115
+ | MCP SSE | `http://<ip>:19851/sse` | Same, SSE transport |
116
+
117
+ ### Discover the device
118
+
119
+ ```
120
+ bricks devtools scan
121
+ bricks devtools open <ip>
122
+ bricks devtools open <ip> --info # show full URLs incl. CDP/MCP/passcode
123
+ ```
124
+
125
+ ### CDP commands against the device
126
+
127
+ All `bricks devtools …` commands accept `-a <address> [-p <port>] [--passcode <pc>]`:
128
+
129
+ ```
130
+ bricks devtools screenshot -a <ip> --passcode <pc> -o on-device.png
131
+ bricks devtools brick tree -a <ip> --passcode <pc> -d 5
132
+ bricks devtools brick query "[short-id='abc']" -a <ip> --passcode <pc>
133
+ bricks devtools brick attributes <nodeId> -a <ip> --passcode <pc>
134
+ bricks devtools brick box-model <nodeId> -a <ip> --passcode <pc>
135
+ bricks devtools input tap 540 960 -a <ip> --passcode <pc>
136
+ bricks devtools input type "test user" -a <ip> --passcode <pc>
137
+ bricks devtools input key Enter -a <ip> --passcode <pc>
138
+ bricks devtools storage data-bank list -a <ip> --passcode <pc>
139
+ bricks devtools storage data-bank get <name> <store> -a <ip> --passcode <pc>
140
+ bricks devtools storage system persist -a <ip> --passcode <pc>
141
+ bricks devtools storage generator-cache -a <ip> --passcode <pc>
142
+ bricks devtools network list -a <ip> --passcode <pc>
143
+ bricks devtools network get <requestId> -a <ip> --passcode <pc>
144
+ bricks devtools runtime eval "Object.keys(window)" -a <ip> --passcode <pc>
145
+ ```
146
+
147
+ ### Bridge the device into Claude Code via MCP
148
+
149
+ For interactive agent-driven verification, bridge the device's MCP endpoint as a local STDIO server with mcporter:
150
+
151
+ ```
152
+ npx mcporter --url http://<ip>:19851/mcp --header "Authorization: Bearer <passcode>"
153
+ ```
154
+
155
+ Configure Claude Code (or another MCP client) to spawn that command as an MCP server. The agent can then call the device's MCP tools the same way it calls `bricks-ctor` MCP tools.
156
+
157
+ ### Real-device side-effects warning
158
+
159
+ Real devices fire real peripherals. Payment terminals charge. MQTT broadcasts on shared topics. BLE advertises to bystanders. Use a **staging** device for verification cycles; never iterate on a production-deployed device unless the user explicitly approves.
160
+
161
+ ## Path 3 — Remote debugging via BRICKS Controller (off-LAN)
162
+
163
+ Out of scope for the design loop. It exists for ops scenarios where the device is not on the same network. If the user asks for it, point them at the BRICKS Controller documentation rather than attempting it as part of design verification.
164
+
165
+ ## Decision rule
166
+
167
+ ```
168
+ default Path 1.
169
+
170
+ if deployment depends on:
171
+ physical orientation / DPI / touch HW / peripherals /
172
+ watchdog on real launcher / LocalSync / certified hardware
173
+ → escalate to Path 2 before declaring done.
174
+
175
+ if brief touches payment, identity, peripherals, LocalSync
176
+ → Path 2 is mandatory, not optional.
177
+
178
+ if user is off-LAN
179
+ → Path 3 (out of scope here).
180
+ ```
181
+
182
+ ## What to actually verify (per shape)
183
+
184
+ ### Static signage (single-canvas glance loop)
185
+ - Path 1 screenshot captures the loop frame.
186
+ - Watch one full rotation in `bun preview` to see all queue items.
187
+ - Cut network mid-loop; confirm cached media plays.
188
+ - Path 2 only if the panel is OLED / has burn-in concerns or specific color profile.
189
+
190
+ ### Multi-canvas state machine (kiosk)
191
+ - Path 1: Automation walking the happy path with `brick_press` + `wait_until_canvas_change` + `assert_property` + `match_screenshot` per Canvas.
192
+ - Cancel + inactivity reset paths verified as Automations.
193
+ - Watchdog reset: kill the runtime, restart, confirm boot Canvas resets transient flow Data.
194
+ - Path 2 mandatory if payment / identity is in the flow — fire a real test transaction on staging hardware.
195
+
196
+ ### Subspace-driven composition
197
+ - Path 1: open the Subspace standalone if the preview supports it; verify in isolation.
198
+ - Embedded test: walk the host's flow; assert Outlets fire as expected via `wait_property_update`.
199
+ - Re-mount test: cause the host to re-bind the Subspace; confirm internal state resets.
200
+
201
+ ### Generator-driven reactive
202
+ - Path 1: drive the source via `runtime eval` writes to Data; observe Brick re-render.
203
+ - Throttle / firehose tests: write 100 updates rapidly; confirm the canvas doesn't choke (frame budget intact).
204
+ - Disconnect: simulate source disconnect; confirm UI surfaces stale state, not blank.
205
+ - Threshold transitions: cross every Switch boundary; confirm visual reaction.
206
+ - Path 2 mandatory if the source is a real peripheral — Electron cannot fake the timing or the failure modes.
207
+
208
+ ### Multi-device LocalSync
209
+ - Path 2 mandatory. Two devices on the same LAN with DevTools on. Drive one, observe the other.
210
+
211
+ ## Browser / runtime log discipline
212
+
213
+ Throughout: the DevTools console must be clean. 404s on media, "Data key not defined" warnings, Generator init failures, React-style warnings — every one is a defect or accept-with-comment. Don't ship around them.