@fugood/bricks-ctor 2.25.0-beta.5 → 2.25.0-beta.50

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 (185) hide show
  1. package/compile/__tests__/config-diff.test.js +100 -0
  2. package/compile/__tests__/index.test.js +365 -0
  3. package/compile/__tests__/util.test.js +317 -0
  4. package/compile/action-name-map.ts +64 -0
  5. package/compile/config-diff.ts +155 -0
  6. package/compile/index.ts +273 -32
  7. package/compile/util.ts +26 -7
  8. package/package.json +7 -3
  9. package/skills/bricks-ctor/SKILL.md +23 -17
  10. package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
  11. package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +18 -0
  12. package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
  13. package/skills/bricks-ctor/references/buttress.md +245 -0
  14. package/skills/bricks-ctor/references/data-calculation.md +239 -0
  15. package/skills/bricks-ctor/references/simulator.md +132 -0
  16. package/skills/bricks-ctor/references/source-editing-tools.md +81 -0
  17. package/skills/bricks-ctor/references/verification-toolchain.md +200 -0
  18. package/skills/bricks-design/SKILL.md +150 -45
  19. package/skills/bricks-design/references/architecture-truths.md +132 -0
  20. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  21. package/skills/bricks-design/references/design-critique.md +195 -0
  22. package/skills/bricks-design/references/design-languages.md +265 -0
  23. package/skills/bricks-design/references/performance.md +116 -0
  24. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  25. package/skills/bricks-design/references/translating-inputs.md +152 -0
  26. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  27. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  28. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  29. package/skills/bricks-design/references/workflow.md +134 -0
  30. package/skills/bricks-ux/SKILL.md +114 -0
  31. package/skills/bricks-ux/references/accessibility.md +162 -0
  32. package/skills/bricks-ux/references/flow-states.md +175 -0
  33. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  34. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  35. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  36. package/skills/bricks-ux/references/user-journey.md +168 -0
  37. package/skills/bricks-ux/references/ux-critique.md +256 -0
  38. package/tools/__tests__/_cli-error.test.ts +35 -0
  39. package/tools/_cli-error.ts +17 -0
  40. package/tools/_edits-log.ts +41 -0
  41. package/tools/_git-author.ts +10 -2
  42. package/tools/_last-pushed-commit.ts +28 -0
  43. package/tools/_shell.ts +8 -1
  44. package/tools/deploy.ts +17 -6
  45. package/tools/mcp-env.ts +13 -0
  46. package/tools/mcp-server.ts +8 -0
  47. package/tools/mcp-tools/__tests__/data-calc-editing.test.js +516 -0
  48. package/tools/mcp-tools/__tests__/entry-editing.test.js +866 -0
  49. package/tools/mcp-tools/__tests__/huggingface.test.ts +49 -0
  50. package/tools/mcp-tools/__tests__/icons.test.ts +21 -0
  51. package/tools/mcp-tools/__tests__/mcp-env.test.js +19 -0
  52. package/tools/mcp-tools/_editing-helpers.ts +58 -0
  53. package/tools/mcp-tools/_verify.ts +50 -0
  54. package/tools/mcp-tools/compile.ts +21 -9
  55. package/tools/mcp-tools/data-calc-editing.ts +1349 -0
  56. package/tools/mcp-tools/entry-editing.ts +2336 -0
  57. package/tools/mcp-tools/huggingface.ts +23 -13
  58. package/tools/mcp-tools/icons.ts +23 -7
  59. package/tools/mcp-tools/media.ts +4 -1
  60. package/tools/postinstall.ts +80 -3
  61. package/tools/pull.ts +93 -22
  62. package/tools/push-config.ts +114 -0
  63. package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
  64. package/tools/simulator-preload.cjs +16 -0
  65. package/tools/{preview.ts → simulator.ts} +4 -4
  66. package/types/{animation.ts → animation.d.ts} +24 -8
  67. package/types/{automation.ts → automation.d.ts} +16 -20
  68. package/types/{brick-base.ts → brick-base.d.ts} +1 -1
  69. package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
  70. package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
  71. package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
  72. package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
  73. package/types/bricks/{Image.ts → Image.d.ts} +21 -9
  74. package/types/bricks/{Items.ts → Items.d.ts} +7 -7
  75. package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
  76. package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
  77. package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
  78. package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
  79. package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
  80. package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
  81. package/types/bricks/Scene3D.d.ts +676 -0
  82. package/types/bricks/{Sketch.ts → Sketch.d.ts} +6 -6
  83. package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
  84. package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
  85. package/types/bricks/{Text.ts → Text.d.ts} +9 -9
  86. package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
  87. package/types/bricks/{Video.ts → Video.d.ts} +12 -12
  88. package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
  89. package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
  90. package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
  91. package/types/bricks/{index.ts → index.d.ts} +1 -0
  92. package/types/{common.ts → common.d.ts} +3 -6
  93. package/types/data-calc-command/base.d.ts +57 -0
  94. package/types/data-calc-command/collection.d.ts +418 -0
  95. package/types/data-calc-command/color.d.ts +432 -0
  96. package/types/data-calc-command/constant.d.ts +50 -0
  97. package/types/data-calc-command/datetime.d.ts +147 -0
  98. package/types/data-calc-command/file.d.ts +129 -0
  99. package/types/data-calc-command/index.d.ts +13 -0
  100. package/types/data-calc-command/iteratee.d.ts +23 -0
  101. package/types/data-calc-command/logictype.d.ts +190 -0
  102. package/types/data-calc-command/math.d.ts +275 -0
  103. package/types/data-calc-command/object.d.ts +119 -0
  104. package/types/data-calc-command/sandbox.d.ts +66 -0
  105. package/types/data-calc-command/string.d.ts +407 -0
  106. package/types/{data-calc.ts → data-calc.d.ts} +1 -0
  107. package/types/{data.ts → data.d.ts} +4 -2
  108. package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
  109. package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
  110. package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
  111. package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
  112. package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
  113. package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
  114. package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
  115. package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
  116. package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
  117. package/types/{subspace.ts → subspace.d.ts} +1 -1
  118. package/utils/__tests__/calc.test.js +25 -0
  119. package/utils/__tests__/id.test.js +154 -0
  120. package/utils/calc.ts +5 -1
  121. package/utils/data.ts +5 -7
  122. package/utils/event-props.ts +17 -0
  123. package/utils/id.ts +109 -56
  124. package/skills/bricks-ctor/rules/buttress.md +0 -156
  125. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  126. package/skills/bricks-design/LICENSE.txt +0 -180
  127. package/types/data-calc-command.ts +0 -7005
  128. /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
  129. /package/skills/bricks-ctor/{rules → references}/media-flow.md +0 -0
  130. /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
  131. /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
  132. /package/types/{canvas.ts → canvas.d.ts} +0 -0
  133. /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
  134. /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
  135. /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
  136. /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
  137. /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
  138. /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
  139. /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
  140. /package/types/generators/{File.ts → File.d.ts} +0 -0
  141. /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
  142. /package/types/generators/{Http.ts → Http.d.ts} +0 -0
  143. /package/types/generators/{HttpServer.ts → HttpServer.d.ts} +0 -0
  144. /package/types/generators/{Information.ts → Information.d.ts} +0 -0
  145. /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
  146. /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
  147. /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
  148. /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
  149. /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
  150. /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
  151. /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
  152. /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
  153. /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
  154. /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
  155. /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
  156. /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
  157. /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
  158. /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
  159. /package/types/generators/{Question.ts → Question.d.ts} +0 -0
  160. /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
  161. /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
  162. /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
  163. /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
  164. /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
  165. /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
  166. /package/types/generators/{Step.ts → Step.d.ts} +0 -0
  167. /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
  168. /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
  169. /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
  170. /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
  171. /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
  172. /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
  173. /package/types/generators/{Tick.ts → Tick.d.ts} +0 -0
  174. /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
  175. /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
  176. /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
  177. /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
  178. /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
  179. /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
  180. /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
  181. /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
  182. /package/types/generators/{index.ts → index.d.ts} +0 -0
  183. /package/types/{index.ts → index.d.ts} +0 -0
  184. /package/types/{switch.ts → switch.d.ts} +0 -0
  185. /package/types/{system.ts → system.d.ts} +0 -0
@@ -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 `bricks-ctor/rules/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 the available preview tool, 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,284 @@
1
+ # When the Brief is Branded — Media Flow Protocol
2
+
3
+ Brand fidelity is *the* differentiator on branded scene work. Every branded BRICKS deployment that "looks generic" got there by skipping the asset hunt and going straight to colors-and-fonts. Most agent-produced branded work fails at the same stop: the agent describes assets it should have, then proceeds to design without acquiring them.
4
+
5
+ This file is operational, not aspirational. It tells the agent **what to actually do**, with concrete recipes, a quality bar with named dimensions, structural enforcement that prevents redraw shortcuts, and a sanctioned path for *creating* assets when none exist — by composing the host environment's image / motion generators, not by drawing in Sketch.
6
+
7
+ In BRICKS, brand binaries live in **Media Flow** and are referenced from Subspaces by Data of `kind: { type: 'media-resource-image' | 'media-resource-video' | 'media-resource-audio' | 'media-resource-file' | 'lottie-file-uri' | 'rive-file-uri' | 'binary-asset' }`. **Never embed brand binaries inline in Brick props.** The Application config travels light; binaries refresh on a different cadence; reuse demands an indirection.
8
+
9
+ **Media Flow is offline-safe by design.** Assets bound this way are preloaded to the device with hash-verified integrity and served from local storage thereafter. Image, video, Lottie, Rive, brand reels — none of these are "offline risk" categories when bound correctly. Do not water down a branded design with text-only fallbacks because the deployment is offline; richness through Media Flow is precisely how the runtime supports offline-first scenes. (See [`performance.md`](performance.md#media-in-media-flow-is-local-first--imagevideolottierive-does-not-break-offline) for the full mechanism.)
10
+
11
+ ## Asset categories — first-class vs. auxiliary
12
+
13
+ The single most common branded-work failure: agent collects colors and fonts, calls it a brand spec, and ships generic-looking output. Brand identity is *recognized*, not *measured* — and recognition runs on:
14
+
15
+ | Asset type | Recognition contribution | Required when |
16
+ |---|---|---|
17
+ | **Logo** (vector, light + dark + mono) | Highest — any scene with a logo is instantly identifiable | Always, for any branded work |
18
+ | **Product photography** (real renders, multiple crops) | Very high — physical products are recognized by themselves | Physical-product brands (hardware, consumer goods, hospitality, retail) |
19
+ | **UI screenshots** (real captures, current version) | Very high — digital products are recognized by their interface | App / SaaS / dashboard / portal brands |
20
+ | **Scene / lifestyle photography** (brand-shot, real venues) | High — sets the brand's environmental temperature | Hospitality / retail / experiential / real-estate brands |
21
+ | **Motion assets** (brand video, Lottie, Rive) | High — BRICKS canvases are time-based; brand motion is first-class | Any brand with extant motion identity (launch films, brand reels, animated logos) |
22
+ | **Color palette** | Medium — supporting role; orphans without the above | All work |
23
+ | **Typography** | Medium — supporting role; same caveat | All work |
24
+ | **Voice & tone** | High where used (constraint on visible Data) | Where copy is non-trivial |
25
+
26
+ **Rule that follows:** acquiring colors and fonts only — without logo / product / UI / motion — is **not** a brand spec. It is a partial spec at best. Surface the missing categories as gaps before you start designing.
27
+
28
+ ## The acquisition flow — five steps, each with concrete actions
29
+
30
+ ### Step 1 — Ask, with a full checklist
31
+
32
+ Don't ask "do you have brand guidelines?". That gets a yes from people who only have a logo and a hex code, and the gap stays invisible until you're already designing. Ask explicitly, in one batch:
33
+
34
+ ```
35
+ For <brand>, do you have any of these? (priority order)
36
+ 1. Logo — vector source (SVG / AI / PDF) or high-res PNG. Light + dark + mono variants ideal.
37
+ 2. Product photography (physical brands) — official renders or real photos, multiple crops.
38
+ 3. UI screenshots (digital brands) — current-version captures of key screens.
39
+ 4. Scene / lifestyle photography (hospitality / retail / experiential).
40
+ 5. Motion assets — brand films, animated logos, Lottie / Rive files.
41
+ 6. Brand colors — palette + usage rules.
42
+ 7. Brand fonts — files licensed for embedded display, or named alternatives.
43
+ 8. Brand book / design system / voice guide.
44
+
45
+ Send what you have; I'll source the rest from official channels and tell you what's missing.
46
+ ```
47
+
48
+ ### Step 2 — Search official channels first
49
+
50
+ Before any aggregator:
51
+
52
+ | Asset | Primary channels |
53
+ |---|---|
54
+ | Logo | `<brand>.com/brand`, `<brand>.com/press`, `<brand>.com/press-kit`, `brand.<brand>.com`, the live site's header inline-`<svg>` |
55
+ | Product photos | `<brand>.com/<product>` hero + gallery, official launch films (capture frames), official press releases |
56
+ | UI screenshots | App Store / Play Store product page captures, the live site's screenshots section, official demo videos (capture frames) |
57
+ | Scene / lifestyle | Brand campaign archive on the live site, brand's official social media accounts |
58
+ | Motion | Brand's official YouTube channel, brand's launch microsites, the brand's own design-system Lottie library |
59
+ | Colors | Brand book PDF, brand design-system repo, the live site's CSS variables / Tailwind config |
60
+ | Fonts | The live site's `<link rel="stylesheet">` references, Google Fonts trace, brand book |
61
+
62
+ **`WebSearch` fallback queries** when official is dry:
63
+ - `<brand> logo download SVG`, `<brand> press kit`
64
+ - `<brand> <product> official renders`, `<brand> product photography hi-res`
65
+ - `<brand> app screenshots`, `<brand> dashboard UI`
66
+ - `<brand> launch film`, `<brand> brand reel`
67
+
68
+ Aggregators (Brandfetch, Logo.dev, etc.) are placeholders, not sources of truth. When you use one, label it as such in the spec and continue searching for the official source in parallel.
69
+
70
+ ### Step 3 — Acquire, with three fallback paths per category
71
+
72
+ Each category has a fallback ladder by yield. Walk down the ladder; don't stop on the first hit.
73
+
74
+ #### Logo — never approximate
75
+
76
+ 1. Independent SVG / PNG file from the brand's press kit.
77
+ 2. Inline `<svg>` extracted from the official site's HTML (`curl -A "Mozilla/5.0" -L <url>` then locate the logo node).
78
+ 3. Official social-media avatar (typically 400×400 or 800×800, transparent PNG) — last resort.
79
+
80
+ If all three fail: **stop and ask the user**. A logo is not optional; it's the recognition root. Do not draw a Sketch-Brick imitation. Do not generate a "logo-shaped" thing with AI. Either acquire a real logo or escalate the gap.
81
+
82
+ #### Product photography (physical brands)
83
+
84
+ 1. Official product page hero image — usually 2000 px+; fetch directly.
85
+ 2. Official press kit / brand center — often the highest resolution available.
86
+ 3. Official launch video frame-grab — for products with strong launch content. Use the host's video tools or extract via `yt-dlp` + `ffmpeg` if available.
87
+ 4. Wikimedia Commons — public-domain or freely licensed for some products.
88
+ 5. **AI generation, brand-anchored** — see [§ Creating assets when missing](#creating-assets-when-missing). Allowed only as a last resort and only with the discipline below.
89
+
90
+ #### UI screenshots (digital brands)
91
+
92
+ 1. App Store / Play Store product captures (verify they're real screenshots, not mockup-generator output).
93
+ 2. The live site's screenshots section.
94
+ 3. Demo-video frame-grabs.
95
+ 4. The brand's own social posts at recent product launches (often the freshest captures).
96
+ 5. The user's own account, captured live. Most accurate when the user has access.
97
+
98
+ #### Scene / lifestyle photography
99
+
100
+ 1. Brand campaign archive on official channels.
101
+ 2. Authorized licensed library (Getty / brand-specific contracted libraries) where the user has access.
102
+ 3. AI generation with reference anchoring — last resort.
103
+
104
+ #### Motion assets
105
+
106
+ 1. Brand's official channel (YouTube, Vimeo) — download with permission, frame-grab where embedding the full clip is overkill.
107
+ 2. Lottie / Rive files in the brand's design-system repo, where they exist.
108
+ 3. AI motion generation with reference anchoring — last resort, very high failure rate, expect to discard 9 of 10 candidates.
109
+
110
+ #### Colors and fonts (auxiliary)
111
+
112
+ - Colors: brand book → live-site CSS → sampling from official imagery (last resort, document the sample point).
113
+ - Fonts: brand-issued license + files → documented alternative if the license isn't available → closest licensed family with a note.
114
+
115
+ ### Step 4 — Score every asset against the 5-10-2-8 bar
116
+
117
+ The bar exists because cosmetic-quality drift is invisible until shipped, then obvious. Without it, the agent rates every found asset 8/10 and the floor sinks.
118
+
119
+ **Quantity bar:** search **5** rounds across multiple channels; collect at least **10** candidates per non-logo category before filtering; keep **2** winners; each scoring **≥ 8/10**.
120
+
121
+ **Quality dimensions** — for each candidate, score 0–10 on each, take the average. Below 8 → discard or use as a labeled placeholder. Logo is exempt from this filter (any real logo beats no logo).
122
+
123
+ | Dimension | What 8+ looks like | What's a deduction |
124
+ |---|---|---|
125
+ | **Resolution** | ≥ 2× the largest render size; for signage / 4K / print ≥ 3000 px on the long edge | Pixelation visible at target render scale; aliasing on transparent edges |
126
+ | **Provenance** | Official source, dated, license-clear; press-kit asset > brand-controlled aggregator > public-domain repository | Aggregator-only, undated, or third-party-redistributed; suspected scraped copy |
127
+ | **Brand fit** | Matches the spec's vibe keywords; matches campaign era you're working from | Off-era (2018 logo for a 2024 rebrand); wrong product variant; sub-brand bleed |
128
+ | **Composition / craft** | Lighting, framing, background all consistent with the rest of the asset set | Mixed lighting across "set"; orphan background colors; visible artifacts |
129
+ | **Narrative weight** | Carries one story alone; can be a hero on its own merits | Only works as decoration / filler; needs surrounding chrome to read |
130
+
131
+ **Logging the scores** in `brand-spec.md` (per category) is what makes the bar real. If you didn't log a score, you didn't apply the bar.
132
+
133
+ ### Step 5 — Bind into Media Flow + persist `brand-spec.md`
134
+
135
+ Upload acquired assets into a Media Flow workspace (use the host's Media Flow MCP tools where available — `list_media_workspaces`, `get_media_workspace`, `media_box_upload`, `update_media_file_meta`). Reference each from the Subspace via Data with the appropriate `kind`:
136
+
137
+ - Image → `kind: { type: 'media-resource-image', preload: { type: 'url', hashType, hash } }`
138
+ - Video → `media-resource-video`
139
+ - Audio → `media-resource-audio`
140
+ - Lottie → `lottie-file-uri`
141
+ - Rive → `rive-file-uri`
142
+ - General binary → `media-resource-file` or `binary-asset`
143
+
144
+ Bricks bind via DataLink: `Image.property.source = linkData(() => brandLogoData)`. Refreshing an asset is a Media Flow operation; the Subspace doesn't need to know the URL changed.
145
+
146
+ Fonts: declare each as an `ApplicationFont` entry in `Application.applicationSettings.fonts` (`name`, `url`, optional `md5`). Reference by name in Brick `property.fontFamily`.
147
+
148
+ `brand-spec.md` lives next to the Subspace. Use this template verbatim — every section is required, even the gap section:
149
+
150
+ ```markdown
151
+ # Brand spec — <brand name>
152
+ > Captured: <YYYY-MM-DD>
153
+ > Sources: <list>
154
+ > Coverage: <complete / partial / inferred>
155
+
156
+ ## First-class assets
157
+
158
+ ### Logo
159
+ - Primary (dark ground): <Media Flow ID> → Data: brandLogoDark
160
+ - Primary (light ground): <Media Flow ID> → Data: brandLogoLight
161
+ - Mono / reverse: <Media Flow ID> → Data: brandLogoMono
162
+ - Clearspace: <e.g., 1× cap-height>
163
+ - Minimum size: <e.g., 96 px wide>
164
+ - Source: <press-kit URL / inline-svg from <url> / file from user>
165
+
166
+ ### Product photography (if physical brand)
167
+ - Hero front: <Media Flow ID> → Data: productHero (3000×2000, transparent) [score: R9 P9 B8 C9 N9]
168
+ - Detail crop A: ... [score: ...]
169
+ - Scene context: ... [score: ...]
170
+
171
+ ### UI screenshots (if digital brand)
172
+ - Home: ... [score: ...]
173
+ - Core feature A: ... [score: ...]
174
+
175
+ ### Scene / lifestyle (if relevant)
176
+ - ...
177
+
178
+ ### Motion
179
+ - Brand reel clip: <Media Flow ID> → Data: brandReel (1920×1080, 12s, h264)
180
+ - Animated logo: <Media Flow ID> → Data: brandLogoLottie
181
+
182
+ ## Auxiliary
183
+
184
+ ### Colors (theme tokens — exposed as Data when shared)
185
+ - --brand-primary #XXXXXX OKLCH(...) source: <where>
186
+ - --brand-accent #XXXXXX
187
+ - --brand-neutral-100 / -900
188
+ - Forbidden: <colors the brand explicitly does not use>
189
+
190
+ ### Fonts (ApplicationFont entries)
191
+ - Display: <family / weights / file location / license>
192
+ - Body: <family / weights / file location / license>
193
+
194
+ ### Voice & tone
195
+ - Register: <e.g., confident, terse, never effusive>
196
+ - Capitalization: <e.g., sentence case>
197
+ - Banned phrases: <e.g., "innovative", "revolutionary", "AI-powered">
198
+
199
+ ### Signature details
200
+ - <One or two design moves the brand is known for — a specific corner radius, a hairline rule, a particular typographic treatment.>
201
+
202
+ ## Gaps & placeholders
203
+ - <category>: <why missing> → <action: ask user / AI generate / accept placeholder> [as of <date>]
204
+
205
+ ## Score log (for non-logo assets)
206
+ - <asset>: R<n> P<n> B<n> C<n> N<n> = <avg>/10
207
+ ```
208
+
209
+ ## Structural enforcement — assets are referenced, not redrawn
210
+
211
+ Once `brand-spec.md` exists, the discipline that keeps the design honest is structural, not behavioural. Encode it in the build:
212
+
213
+ - **Every brand visual** is a Brick reading from a Media Flow Data via DataLink. No `Image.property.source` set to a literal URL or path. No `Sketch` Brick drawing a "logo-shaped" graphic. No `Svg` Brick reproducing the logo as a path. The pattern enforces "you cannot ship a redrawn logo because the project structure doesn't allow it."
214
+ - **Brand colors used in 2+ places** become theme-token Data, bound via DataLink. Single-use decorative colors can stay hardcoded (Truth #2's loose convention).
215
+ - **Theme tokens are the single source of truth.** Want to add a new color? Add a Data entry first, then bind. The "first" is what makes it a system instead of an accumulation.
216
+ - **Every Brick that uses a brand asset** has its source traceable to a `brand-spec.md` line. Reviewing the spec is reviewing the design.
217
+
218
+ ## Creating assets when missing
219
+
220
+ When a category has no acquirable real asset and the user has no plan to provide one, **do not** quietly substitute a placeholder and proceed. Three options, in priority order:
221
+
222
+ 1. **Stop and ask.** For logo, this is the only allowed option.
223
+ 2. **Compose the host environment's generative tools.** If a canvas-design / imagen / image-generation MCP / motion-generation MCP / Banana-style tool is available in the host, use it — *anchored on a verified brand reference*. This skill does not duplicate them; it composes them.
224
+ 3. **Accept a labeled placeholder Brick** with the gap tracked in `brand-spec.md`. Use this when generation is unavailable or known to fail for the asset type (e.g., generating a "real product photo" for a niche industrial product almost always fails).
225
+
226
+ ### Generative discipline (when option 2)
227
+
228
+ - **Anchor on real brand material.** Feed at least one verified brand asset — the logo, a verified product photo, a brand color sample — as conditioning to the generator. Generation from "the brand's general vibe" produces uncanny-valley assets that feel adjacent to the brand without being the brand. Almost always recognized as fake by reviewers.
229
+ - **Generate at the 5-10-2-8 quantity bar.** 8–10 candidates per slot, scored on the same 5 dimensions, keep 2.
230
+ - **Persist generation metadata** in `brand-spec.md`: prompt, seed (where supported), reference assets used, model version. Reproducibility matters when the user later asks for a refresh in the same style.
231
+ - **Never generate logos, wordmarks, or named characters.** These are identity, not imagery — generation produces look-alikes that legal will reject.
232
+ - **Avoid AI-design tells.** Gradient orbs as "AI". Glassy translucent panels with no source. Generic happy-diverse-people-in-a-bright-office. Tech-y blue-purple gradients as backgrounds. Float / hover dot patterns. Watch for these in generated output and re-generate or discard.
233
+ - **Motion generation is high-failure.** For Lottie / Rive / video, expect to discard 9 of 10 outputs. Budget the iteration; don't ship the first plausible result.
234
+
235
+ ### What "compose, don't rebuild" looks like in practice
236
+
237
+ - This skill does **not** teach typography pairing, color theory, image composition, or generative prompting from scratch. Those domains are deep and other skills cover them well.
238
+ - This skill **does** insist that when the host has a canvas-design / imagen / image-generation skill available, the agent invoke it for the relevant sub-task — generation, motion, composition — rather than substitute its own freehand work or skip with a placeholder.
239
+ - Where the host has none, surface that constraint to the user. "There's no image generator available in this environment; we have three options: (a) you supply, (b) we accept labeled placeholders, (c) you bring a generator skill / MCP into the environment." Don't silently degrade.
240
+
241
+ ## Anti-patterns — recurring failures
242
+
243
+ - **Colors-and-fonts-only branding.** Visual identity lives in photography and logo treatment, not hex codes. If logo + product/scene aren't acquirable, surface it as a blocker before continuing.
244
+ - **Aggregator-as-source-of-truth.** Brandfetch is a placeholder; not a final source.
245
+ - **Eyeballing a hero color from a competitor's site or a stylistic neighbour.** Leaks the wrong brand into the work; reviewers catch it immediately.
246
+ - **CSS-drawn imitations of real assets.** A gradient orb is not a logo. A Sketch path is not a product photo. Source or generate-with-anchor or labeled-placeholder. There is no fourth option.
247
+ - **Embedding base64 in Brick props.** Pollutes the Subspace, defeats Media Flow refresh, balloons the file. Lift to Media Flow.
248
+ - **Generating without a brand-reference anchor.** Produces uncanny-valley assets adjacent to the brand. Always anchor.
249
+ - **Demo-content color contamination.** A Stripe-style hero from an aggregator site that has a third-party brand's accent color baked in. Either request a clean asset or note the contamination in the spec.
250
+ - **Skipping the score log.** Without it, the bar is aspirational. Log scores per non-logo asset; commit the log alongside the spec.
251
+
252
+ ## When the brand has nothing
253
+
254
+ Some BRICKS deployments are for venues with no prior brand work — small clinics, community lobbies, one-off pop-ups. In that case:
255
+
256
+ - Skip the protocol; admit the gap up front.
257
+ - Switch into Direction Advisor mode ([`when-the-brief-is-vague.md`](when-the-brief-is-vague.md)) and propose 3 differentiated visual directions.
258
+ - The picked direction *becomes* the brand spec for this Application. Persist the picked direction's tokens, motion language, photography style guidelines, and (if any) generated assets via the same `brand-spec.md` template — even when invented from scratch. Treat it as a real spec, not a working note. The next person to touch the deployment will need it.
259
+
260
+ ## Voice & tone as a constraint on user-visible Data
261
+
262
+ Voice and tone don't live in Bricks; they live in the strings inside Data values. The brand spec's voice section becomes a **content-author constraint**:
263
+
264
+ - All user-visible Data values follow the register (sentence case, no exclamation marks, banned phrases avoided, length budget respected).
265
+ - Translations follow the register in their target language — work with a localizer who understands the voice.
266
+ - Generator-produced strings (LLM responses, formatted dates, error messages) need explicit prompts or templates that respect the voice. Do not trust default LLM phrasing; constrain it.
267
+
268
+ Surface this to whoever maintains content post-deploy so it doesn't quietly drift.
269
+
270
+ ## Coverage checklist before declaring asset work complete
271
+
272
+ You cannot move from this protocol back into design until all of the following are true:
273
+
274
+ 1. Logo — present in Media Flow with light + dark + mono where applicable; bound via Data.
275
+ 2. Product / UI / scene category appropriate to the brand type — present, scored ≥ 8/10 on each kept asset, bound via Data.
276
+ 3. Motion category — present where the brand has motion identity; absent and noted otherwise.
277
+ 4. Colors — palette declared as theme-token Data (for shared colors) and / or as `brand-spec.md` entries.
278
+ 5. Fonts — declared as `ApplicationFont` entries; license confirmed.
279
+ 6. Voice & tone — captured as constraint or marked TBD with stakeholder + date.
280
+ 7. Gaps — every missing or placeholder asset listed under "Gaps & placeholders" with action and date.
281
+ 8. Score log — every non-logo asset scored on the 5 dimensions and logged.
282
+ 9. Spec committed — `brand-spec.md` exists alongside the Subspace; not in a chat scrollback or a working file that will be lost.
283
+
284
+ If any item is incomplete, name it in your reply and either resolve it or pause for user input. Don't proceed silently.