@fugood/bricks-ctor 2.24.9 → 2.24.11
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.
- package/compile/index.ts +34 -12
- package/package.json +2 -2
- package/skills/bricks-ctor/references/architecture-patterns.md +1 -0
- package/skills/bricks-ctor/references/data-calculation.md +13 -0
- package/skills/bricks-ctor/references/media-flow.md +7 -0
- package/skills/bricks-ctor/references/verification-toolchain.md +9 -0
- package/skills/bricks-design/SKILL.md +9 -7
- package/skills/bricks-design/references/design-critique.md +8 -8
- package/skills/bricks-design/references/design-languages.md +24 -18
- package/skills/bricks-design/references/design-md.md +225 -0
- package/skills/bricks-design/references/translating-inputs.md +9 -2
- package/skills/bricks-design/references/variations-and-tweaks.md +1 -1
- package/skills/bricks-design/references/when-the-brief-is-branded.md +29 -30
- package/skills/bricks-design/references/workflow.md +13 -17
- package/tools/mcp-tools/compile.ts +2 -0
- package/tools/mcp-tools/simulator-guidance.ts +31 -0
- package/tools/pull.ts +15 -9
- package/types/bricks/Items.d.ts +4 -0
- package/types/bricks/Sketch.d.ts +4 -2
- package/types/bricks/Video.d.ts +68 -1
- package/types/generators/Http.d.ts +6 -1
- package/types/generators/Tick.d.ts +1 -1
- package/utils/data.ts +1 -1
- package/utils/event-props.ts +18 -2
package/compile/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import upperFirst from 'lodash/upperFirst'
|
|
|
4
4
|
import snakeCase from 'lodash/snakeCase'
|
|
5
5
|
import omit from 'lodash/omit'
|
|
6
6
|
import { parse as parseAST } from 'acorn'
|
|
7
|
-
import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
|
|
7
|
+
import type { BlockStatement, ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
|
|
8
8
|
import escodegen from 'escodegen'
|
|
9
9
|
import { makeSeededId } from '../utils/id'
|
|
10
10
|
import { generateCalulationMap } from './util'
|
|
@@ -97,17 +97,28 @@ const compileProperty = (property, errorReference: string, result = {}) => {
|
|
|
97
97
|
const compileScriptCalculationCode = (code = '') => {
|
|
98
98
|
try {
|
|
99
99
|
const program = parseAST(code, { sourceType: 'module', ecmaVersion: 2020 })
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
// The stored config holds the bare function body, which codegen re-wraps as
|
|
101
|
+
// `export function main() { <code> }`. Unwrap it back to that body here.
|
|
102
|
+
let block = ((program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration)
|
|
103
|
+
?.body as BlockStatement | undefined
|
|
104
|
+
if (!block) return code || ''
|
|
105
|
+
// Earlier versions emitted the whole BlockStatement (braces included), so every
|
|
106
|
+
// compile -> codegen round-trip nested the body in one more `{ }`. Emit the inner
|
|
107
|
+
// statements instead, collapsing any wrapper blocks previous round-trips added so
|
|
108
|
+
// existing over-wrapped sandboxes heal on the next compile.
|
|
109
|
+
while (block.body.length === 1 && block.body[0].type === 'BlockStatement') {
|
|
110
|
+
block = block.body[0] as BlockStatement
|
|
111
|
+
}
|
|
112
|
+
return escodegen.generate(
|
|
113
|
+
{ type: 'Program', body: block.body },
|
|
114
|
+
{
|
|
115
|
+
format: {
|
|
116
|
+
indent: { style: ' ' },
|
|
117
|
+
semicolons: false,
|
|
118
|
+
},
|
|
119
|
+
comment: true,
|
|
108
120
|
},
|
|
109
|
-
|
|
110
|
-
})
|
|
121
|
+
)
|
|
111
122
|
} catch {
|
|
112
123
|
return code || ''
|
|
113
124
|
}
|
|
@@ -709,6 +720,17 @@ const compileAutomation = (automationMap: AutomationMap) =>
|
|
|
709
720
|
}),
|
|
710
721
|
)
|
|
711
722
|
|
|
723
|
+
const buildDefaultExpandedState = (subspace: Subspace) => ({
|
|
724
|
+
brick: false,
|
|
725
|
+
generator: true,
|
|
726
|
+
canvas: (subspace.canvases || []).reduce((acc, canvas) => {
|
|
727
|
+
if (canvas?.id) acc[canvas.id] = false
|
|
728
|
+
return acc
|
|
729
|
+
}, {}),
|
|
730
|
+
property_bank: false,
|
|
731
|
+
property_bank_calc: true,
|
|
732
|
+
})
|
|
733
|
+
|
|
712
734
|
export const compile = async (app: Application) => {
|
|
713
735
|
await new Promise((resolve) => setImmediate(resolve, 0))
|
|
714
736
|
const timestamp = Date.now()
|
|
@@ -789,7 +811,7 @@ export const compile = async (app: Application) => {
|
|
|
789
811
|
property_bank: !subspace.unexpanded.data,
|
|
790
812
|
property_bank_calc: !subspace.unexpanded.dataCalculation,
|
|
791
813
|
}
|
|
792
|
-
:
|
|
814
|
+
: buildDefaultExpandedState(subspace),
|
|
793
815
|
layout: {
|
|
794
816
|
width: subspace.layout?.width,
|
|
795
817
|
height: subspace.layout?.height,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-ctor",
|
|
3
|
-
"version": "2.24.
|
|
3
|
+
"version": "2.24.11",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"typecheck": "tsc --noEmit",
|
|
7
7
|
"build": "bun scripts/build.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@fugood/bricks-cli": "^2.24.
|
|
10
|
+
"@fugood/bricks-cli": "^2.24.11",
|
|
11
11
|
"@huggingface/gguf": "^0.3.2",
|
|
12
12
|
"@iarna/toml": "^3.0.0",
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
@@ -25,6 +25,7 @@ The primary way to orchestrate multi-step flows. A single event can contain an a
|
|
|
25
25
|
- Use `waitAsync: true` to await async actions before the next step
|
|
26
26
|
- Use `dataParams` + `mapping` to pass event data downstream
|
|
27
27
|
- This is the "glue" that wires generators, state, and UI together
|
|
28
|
+
- For Generator result UI: await Generator, write done/version Data; route/current Data stays identity.
|
|
28
29
|
|
|
29
30
|
Sequential `PROPERTY_BANK` / `PROPERTY_BANK_EXPRESSION` actions in one chain read the data values that existed when the chain started. If a later action needs to read what an earlier action wrote, set `waitAsync: true` on the earlier action.
|
|
30
31
|
|
|
@@ -87,6 +87,17 @@ const triggerCalc: EventAction = {
|
|
|
87
87
|
- When a **later action reads the calc's outputs**, set `waitAsync: true` on the `PROPERTY_BANK_COMMAND` action itself — it awaits the full calc chain including output writes.
|
|
88
88
|
- A dataParam's `value` acts as an execution gate: `{ input: () => d, value: false }` skips that trigger (combine with `mapping` for conditional runs).
|
|
89
89
|
|
|
90
|
+
## One trigger source per result panel
|
|
91
|
+
|
|
92
|
+
A panel that shows the result of an async action (Generator HTTP/LLM response, request telemetry) reads from a calc whose **trigger should come from a single source**: either the live async outlet, or a completion marker the action chain writes — not both.
|
|
93
|
+
|
|
94
|
+
- **Outlet-triggered** (`{ data: () => dResponse, trigger: true }`): the calc reruns as outlets land. Use when each outlet is only written once its value is final.
|
|
95
|
+
- **Marker-triggered**: run the generator with `waitAsync: true`, then write a `done` Data the calc triggers on. Use when several outlets settle separately and the panel must wait for all of them.
|
|
96
|
+
|
|
97
|
+
Wiring both at once — triggering on the live outlet _and_ gating on a separately-written marker — lets the calc run before the outlets have settled (it renders `undefined`/stale) while the marker path masks it intermittently. A panel that updates "sometimes" usually has two competing triggers: pick one, and order it with `waitAsync` so the trigger fires after the values it reads are written.
|
|
98
|
+
|
|
99
|
+
Scope the panel's calc to the **single value it derives** (the formatted display string), and write sibling status labels — running/done, progress, selected route — imperatively in the event chain with `PROPERTY_BANK`. A calc rewrites _every_ one of its outputs on each run, so a return that omits a key writes `undefined` to that output Data (see [Field Rules](#field-rules-defaults-and-constraints)): folding status labels into a multi-output result calc resets them to `undefined` on any run that returns only the derived value. If a calc genuinely must drive several outputs, return all of their keys every run.
|
|
100
|
+
|
|
90
101
|
## Script Sandbox
|
|
91
102
|
|
|
92
103
|
Scripts run in `use strict` mode as a function body — top-level `return` returns the calc result. No `fetch`, `XMLHttpRequest`, or `require` in any mode: I/O belongs to Generators.
|
|
@@ -220,6 +231,8 @@ const appendHistory: DataCalculationScript = {
|
|
|
220
231
|
| `PROPERTY_BANK_COMMAND` does nothing | Auto calc + `trigger: false` input, or `input` doesn't reference an input Data of the calc | Command a `trigger: true` input (auto) or any input (manual) |
|
|
221
232
|
| Compile error `Not allow duplicate set property id...` | Auto mode with same Data as input and output | Use `triggerMode: 'manual'`, or split into separate Data |
|
|
222
233
|
| Calc reads stale value written earlier in the same chain | Missing `waitAsync: true` on the preceding write | Set `waitAsync: true` on the write action |
|
|
234
|
+
| Result/telemetry panel shows `undefined` or stale data intermittently | Calc triggered by two sources at once (live outlet + a separately-written marker) | Trigger from one source — see [One trigger source per result panel](#one-trigger-source-per-result-panel) |
|
|
235
|
+
| Sibling status Data (selected/progress) resets to `undefined` after a calc runs | Multi-output result calc whose return omitted those keys on that run | Scope the calc to one output and write status labels imperatively, or return every output key each run — see [One trigger source per result panel](#one-trigger-source-per-result-panel) |
|
|
223
236
|
| Works in Simulator, fails on device | V8 vs Hermes/JSC engine difference | Verify on device (Path 2); avoid engine-sensitive parsing |
|
|
224
237
|
| `console.log` shows nothing | Console only emits during DevTools debug sessions | Attach DevTools, or write debug values to an output Data |
|
|
225
238
|
| `Promise`/`setTimeout` undefined, or `Async mode is required` error | `enableAsync: false` | Set `enableAsync: true` |
|
|
@@ -31,6 +31,7 @@ const mediaListGenerator: GeneratorMediaFlow = {
|
|
|
31
31
|
description: '',
|
|
32
32
|
property: {
|
|
33
33
|
boxId: 'promo-box-id',
|
|
34
|
+
passcode: 'box-read-only-passcode',
|
|
34
35
|
},
|
|
35
36
|
outlets: {
|
|
36
37
|
files: () => promoFilesData, // Array of file objects
|
|
@@ -40,6 +41,12 @@ const mediaListGenerator: GeneratorMediaFlow = {
|
|
|
40
41
|
}
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
When hand-writing `GENERATOR_MEDIA_FLOW`, do not use a bare `boxId`. The editor
|
|
45
|
+
flow creates the required authorization, but manual config must include either
|
|
46
|
+
`property.passcode` from a Media Box passcode or `property.accessToken`.
|
|
47
|
+
Without one, devices cannot read the box and the generator will report an
|
|
48
|
+
authorization error.
|
|
49
|
+
|
|
43
50
|
## Property Kinds for Media
|
|
44
51
|
|
|
45
52
|
Link Data properties to Media Flow for asset selection:
|
|
@@ -26,6 +26,15 @@ What does **not** count as done:
|
|
|
26
26
|
|
|
27
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
28
|
|
|
29
|
+
## Debug loop discipline
|
|
30
|
+
|
|
31
|
+
Before cycling on a runtime or Automation bug, establish what you can observe. A green or red Automation alone is often not enough to explain a race or branch miss.
|
|
32
|
+
|
|
33
|
+
- Use one repeatable probe at a time: DevTools state/storage/runtime reads, network/console output, Automation screenshots, or a targeted screenshot after a known event.
|
|
34
|
+
- If branch execution or race timing is unclear, add the smallest temporary log/trace point in the relevant data-calculation script, event handler, or generator path, then compile and rerun the same probe.
|
|
35
|
+
- Do not bounce between CLI, DevTools, and Automation without a hypothesis. State what signal will prove or disprove the next fix before changing code again.
|
|
36
|
+
- Remove temporary traces before declaring done, unless the user asked for persistent diagnostics or the trace is intentionally part of the fix.
|
|
37
|
+
|
|
29
38
|
## Path 1 — Electron preview (no device required)
|
|
30
39
|
|
|
31
40
|
The default loop. Always available; deterministic; no device wear; safe for side-effecting flows because nothing real fires.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: bricks-design
|
|
3
3
|
description: >-
|
|
4
4
|
Visual design discipline for Applications and Subspaces — type, palette, asset
|
|
5
|
-
acquisition, design language, system
|
|
5
|
+
acquisition, design language, DESIGN.md system, visual rhythm, brand. TRIGGER
|
|
6
6
|
for visual / aesthetic / system / style / brand-asset work even when named in
|
|
7
7
|
product terms — slideshow, pitch deck, explainer, kiosk, signage, menu board,
|
|
8
8
|
lobby, wayfinding, retail, museum, transit; translate or rebuild from Figma /
|
|
@@ -40,7 +40,8 @@ How to inspect, by source type:
|
|
|
40
40
|
- **Website / docs site URL** — drive a browser automation tool (browser-MCP, Playwright/Puppeteer-style MCP server, `agent-browser`-equivalent skill, or any host-provided browser tool). Visit the home page, every primary navigation entry, every page the user explicitly named, and every page that looks like it carries the brand's visual signature (hero, product, gallery, customers). Capture a full-page screenshot of each. Then read each screenshot back via the host's image-reading capability so you actually *see* it. If no browser automation is available, ask the user for screenshots before proceeding.
|
|
41
41
|
- **Figma / design-tool link** — use a Figma-MCP / design-tool MCP if available to enumerate every frame and image-export each; otherwise ask the user for PNG exports of every frame. Inspect each image.
|
|
42
42
|
- **HTML project on disk** — serve and screenshot every route via a browser tool, the same way as a public URL. Don't infer visuals from the source HTML/CSS.
|
|
43
|
-
- **PDF / brand book / slide deck** — read every page via the host's PDF-reading capability (page-by-page if the document is long). Do not summarize from a text extraction.
|
|
43
|
+
- **PDF / brand book / slide deck** — read every page via the host's PDF-reading capability (page-by-page if the document is long). Do not summarize from a text extraction. If the host only exposes a cover preview or has no PDF capability, use an installed PDF skill when available. If no PDF skill or local PDF tool is installed and you cannot inspect the PDF fully, stop and respond in chat with the limitation instead of designing from partial evidence. Never proceed from only page 1.
|
|
44
|
+
- **Large rendered PDF pages** — do not hand the vision tool 4-13 MB page PNGs and call the PDF unreadable. Downsample to inspection-sized JPEG/PNG previews, split long PDFs into page batches, and keep a page coverage checklist so every page is either inspected or explicitly marked inaccessible.
|
|
44
45
|
- **Local images / screenshots** — read each one via the image-reading tool.
|
|
45
46
|
- **Video walkthrough** — ask the user to provide key-frame screenshots, or to describe each state explicitly. Do not claim to "watch" a video you cannot actually frame-step.
|
|
46
47
|
|
|
@@ -55,7 +56,7 @@ Pass-based delivery, not sprint-to-finished. Full discipline in [`references/wor
|
|
|
55
56
|
- Branded brief → [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md) (Media Flow protocol).
|
|
56
57
|
- Figma / HTML / screenshot input → [`references/translating-inputs.md`](references/translating-inputs.md).
|
|
57
58
|
- Presentation / slideshow / intro / explainer / storyboard → [`references/presentation-and-slideshow.md`](references/presentation-and-slideshow.md) (Canvas-graph shape decision, hero continuity).
|
|
58
|
-
2. **Pass 0** —
|
|
59
|
+
2. **Pass 0** — Write the project's [`DESIGN.md`](references/design-md.md): named tokens in the frontmatter (type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, hero Brick ids) plus Deployment Context and the Visual Theme section. The grid substrate is given by the runtime (Truth #6); the design decisions sit on top. The Subspace file carries only a one-line pointer to DESIGN.md; every Brick cites its tokens by name.
|
|
59
60
|
3. **Pass 1 — Showcase Canvas lockdown.** Build one Canvas fully lit, verify, show the user. This is the cheapest moment to redirect; do not build the full Canvas graph before sign-off.
|
|
60
61
|
4. **Pass 2** — Build out the full Canvas graph with hero ids shared across Canvases (Truth #3 as narrative principle). Mid-review checkpoint partway through.
|
|
61
62
|
5. **Pass 3** — Polish: spacing-scale adherence, density rhythm, Standby Transition vocabulary consistent, multilingual fits.
|
|
@@ -79,7 +80,7 @@ The load-bearing laws. Each is unpacked in [`references/architecture-truths.md`]
|
|
|
79
80
|
|
|
80
81
|
## Commit to a design language
|
|
81
82
|
|
|
82
|
-
A BRICKS Application that doesn't commit to a design language reads as generic regardless of how well it runs. After Direction Advisor (or directly, when the brief is concrete enough), pick a visual language from [`references/design-languages.md`](references/design-languages.md) — Swiss Editorial / Kenya Hara emptiness / Field.io motion poetics / Brutalist web / Y2K futurist-retro / etc. — and
|
|
83
|
+
A BRICKS Application that doesn't commit to a design language reads as generic regardless of how well it runs. After Direction Advisor (or directly, when the brief is concrete enough), pick a visual language from [`references/design-languages.md`](references/design-languages.md) — Swiss Editorial / Kenya Hara emptiness / Field.io motion poetics / Brutalist web / Y2K futurist-retro / etc. — and commit it as named tokens in the project's [`DESIGN.md`](references/design-md.md). Every subsequent choice cites a token by name; adding a value not already in the tokens requires adding the token first. That's what makes the system structural rather than aspirational.
|
|
83
84
|
|
|
84
85
|
The commitment principle: when unsure, do *more* of what defines the chosen language, not less. A style executed at 30% reads as hesitant; at 80% it reads as deliberate. Soften signature moves and you've abandoned the language for nowhere.
|
|
85
86
|
|
|
@@ -98,9 +99,9 @@ Two reference files cover the second-order rules. Read them when the design is n
|
|
|
98
99
|
|
|
99
100
|
## Asset discipline
|
|
100
101
|
|
|
101
|
-
Brand work succeeds or fails on assets, not on colors. Logo, product photography, UI screenshots, scene photography, and motion assets are first-class — colors and fonts are auxiliary. Acquire them through the protocol in [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md): five steps, the 5-10-2-8 quality bar with five named scoring dimensions, three fallback paths per asset category, and structural enforcement that Bricks reference Media Flow Data via DataLink rather than embed, redraw, or skip.
|
|
102
|
+
Brand work succeeds or fails on assets, not on colors. Logo, product photography, UI screenshots, scene photography, and motion assets are first-class — colors and fonts are auxiliary. Acquire them through the protocol in [`references/when-the-brief-is-branded.md`](references/when-the-brief-is-branded.md): five steps, the 5-10-2-8 quality bar with five named scoring dimensions, three fallback paths per asset category, and structural enforcement that Bricks reference Media Flow Data via DataLink rather than embed, redraw, or skip. The acquired assets, scores, and bindings land in the **Assets & Brand** sections of [`DESIGN.md`](references/design-md.md) — one artifact, not a separate brand spec.
|
|
102
103
|
|
|
103
|
-
When an asset category genuinely cannot be acquired and the user has no plan to provide one, the priority is: stop and ask (always for logo) → compose the host environment's image / motion generators (canvas-design / imagen / generative MCP), brand-reference-anchored, scored at the same bar → labeled placeholder with the gap tracked
|
|
104
|
+
When an asset category genuinely cannot be acquired and the user has no plan to provide one, the priority is: stop and ask (always for logo) → compose the host environment's image / motion generators (canvas-design / imagen / generative MCP), brand-reference-anchored, scored at the same bar → labeled placeholder with the gap tracked under DESIGN.md's **Gaps & placeholders**. **This skill does not duplicate generative or composition skills — it composes them.** Where the host has none, surface the constraint to the user; don't silently degrade.
|
|
104
105
|
|
|
105
106
|
## Anti-slop, terse
|
|
106
107
|
|
|
@@ -130,7 +131,7 @@ Ship a one-paragraph trade-off note alongside so the user can pick or blend with
|
|
|
130
131
|
|
|
131
132
|
A single hero-Canvas screenshot is **not** done. A "looks roughly like the reference" handwave is **not** done. A claim of done without screenshots in evidence is **not** done. If you have not produced and reviewed a screenshot of every Canvas, you are still mid-iteration and must say so explicitly to the user.
|
|
132
133
|
|
|
133
|
-
**Self-critique pass before declaring done** — every Canvas scored on 5 dimensions (system commitment / visual hierarchy / craft / functional fit / originality), anti-slop top-10 swept clean, < 8 scores either fixed or surfaced as accepted trade-offs. See [`references/design-critique.md`](references/design-critique.md). Verification proves it runs; critique proves it's good — both required.
|
|
134
|
+
**Self-critique pass before declaring done** — every Canvas scored on 5 dimensions (system commitment / visual hierarchy / craft / functional fit / originality), checked section by section against [`DESIGN.md`](references/design-md.md) (does every colour, gap, and motion in the render trace to a declared token?), anti-slop top-10 swept clean, < 8 scores either fixed or surfaced as accepted trade-offs. See [`references/design-critique.md`](references/design-critique.md). Verification proves it runs; critique proves it's good — both required.
|
|
134
135
|
|
|
135
136
|
For the toolchain itself — `compile` / `preview` MCP usage, `bun preview` flags, Project Automation cases, on-device DevTools setup, the Path 1/2/3 decision rule, and per-deployment-shape verification checklists — see the `bricks-ctor` skill's `rules/verification-toolchain.md`.
|
|
136
137
|
|
|
@@ -150,6 +151,7 @@ Interaction / flow / journey / affordance / feedback / recovery / accessibility
|
|
|
150
151
|
|
|
151
152
|
| When you need to... | Read |
|
|
152
153
|
|---|---|
|
|
154
|
+
| Write or update the project's design-system artifact (tokens, schema, brand merge) | [`references/design-md.md`](references/design-md.md) |
|
|
153
155
|
| Decide when to ask vs build; pace the work across passes | [`references/workflow.md`](references/workflow.md) |
|
|
154
156
|
| Deepen any of the 10 truths | [`references/architecture-truths.md`](references/architecture-truths.md) |
|
|
155
157
|
| Tune for performance / offline | [`references/performance.md`](references/performance.md) |
|
|
@@ -20,19 +20,19 @@ If a dimension cannot be scored ≥ 8 within the deployment's constraints, surfa
|
|
|
20
20
|
|
|
21
21
|
### 1. System commitment
|
|
22
22
|
|
|
23
|
-
*Does the work cite the declared system (the
|
|
23
|
+
*Does the work cite the declared system (the named tokens in `DESIGN.md`), and does deviation have rationale?*
|
|
24
24
|
|
|
25
25
|
| Score | Rubric |
|
|
26
26
|
|---|---|
|
|
27
|
-
| 9–10 | Every brick property points back to a Data token, an `ApplicationFont` entry
|
|
27
|
+
| 9–10 | Every brick property points back to a DESIGN.md token, a Data token, or an `ApplicationFont` entry. Deviations carry a one-line comment explaining why. |
|
|
28
28
|
| 7–8 | Direction is correct; 1–2 hardcoded values that should be tokens, or 1–2 deviations without rationale. |
|
|
29
29
|
| 5–6 | Visible drift — a third type family slipped in, a fourth color appeared mid-flow, or a brick redrew an asset that exists in Media Flow. |
|
|
30
|
-
| 3–4 |
|
|
31
|
-
| 1–2 | No
|
|
30
|
+
| 3–4 | DESIGN.md is decorative; the work doesn't follow it. |
|
|
31
|
+
| 1–2 | No DESIGN.md, or its tokens contradict the work. |
|
|
32
32
|
|
|
33
33
|
**Audit moves:**
|
|
34
|
-
- Open
|
|
35
|
-
- Pick five values *from* the work (a `backgroundColor` literal, a `fontSize`, a Standby easing, a hex code in a Rect, a brick template choice). For each, check that the value traces back to
|
|
34
|
+
- Open DESIGN.md. Pick five tokens from it (a color, a type size, a Standby duration, a margin, a Brick template). For each, check that the work uses it consistently.
|
|
35
|
+
- Pick five values *from* the work (a `backgroundColor` literal, a `fontSize`, a Standby easing, a hex code in a Rect, a brick template choice). For each, check that the value traces back to a DESIGN.md token or has rationale.
|
|
36
36
|
- If signature moves of the chosen design language (per [`design-languages.md`](design-languages.md)) are absent, score drops regardless of token-discipline. Swiss Editorial without numbered folios and hairline rules is not Swiss Editorial; it's "vaguely minimal."
|
|
37
37
|
|
|
38
38
|
### 2. Visual hierarchy
|
|
@@ -67,7 +67,7 @@ If a dimension cannot be scored ≥ 8 within the deployment's constraints, surfa
|
|
|
67
67
|
|
|
68
68
|
**Audit moves:**
|
|
69
69
|
- **Grid stance match.** Off-grid placements should serve the declared language (Brutalist break, Sagmeister theatrical, Risograph hand-placed). Off-grid that drifted in accidentally — flag and snap. A Swiss Editorial Canvas with a Brick at `(3.5, 11.2)` is a tell.
|
|
70
|
-
- **Spacing-scale adherence.** Open
|
|
70
|
+
- **Spacing-scale adherence.** Open DESIGN.md. Read the declared `spacing` tokens (e.g., `gap-s 2 / gap-m 4 / gap-l 8 / margin 8`). Pick ten inter-Brick gaps across the Canvas — every one must come from that set. Gaps of 7 / 9 / 11 / 13 across siblings = arithmetic drift, the agent picked values ad-hoc instead of from the scale. Snap to the nearest scale value or update the scale (and re-check everywhere).
|
|
71
71
|
- List every distinct colour used in the Canvas. > 4 = deduction (excluding intentional photography palette spreads).
|
|
72
72
|
- List every distinct type family. > 2 = deduction.
|
|
73
73
|
- List every distinct Standby easing / duration combination. > 3 = deduction.
|
|
@@ -155,7 +155,7 @@ Walk this list every time. Each is a pattern that consistently slips into agent-
|
|
|
155
155
|
**Tell:** a brick's `pressable: 'enabled'` carries a press scale-down animation, but the deployment is a 75" no-touch signage panel. **Why bad:** the affordance promises something the hardware cannot deliver; the design reads as "ported from web." **Fix:** drop hover on no-touch; convert to a Switch driven by a non-pointer signal (sensor, timer, peripheral Generator) if the visual intent matters.
|
|
156
156
|
|
|
157
157
|
### 10. AI-generated brand imagery without a real brand-reference anchor
|
|
158
|
-
**Tell:** a generated "brand-appropriate" hero image that nobody at the brand has ever seen, produced from a prompt like "modern tech product on a gradient." **Why bad:** generation without anchoring produces uncanny-valley assets that feel adjacent to the brand without being the brand. **Fix:** anchor every generation on at least one verified brand asset (logo, real photo, official color sample); apply 5-10-2-8; log generation metadata in
|
|
158
|
+
**Tell:** a generated "brand-appropriate" hero image that nobody at the brand has ever seen, produced from a prompt like "modern tech product on a gradient." **Why bad:** generation without anchoring produces uncanny-valley assets that feel adjacent to the brand without being the brand. **Fix:** anchor every generation on at least one verified brand asset (logo, real photo, official color sample); apply 5-10-2-8; log generation metadata in DESIGN.md's Assets & Brand. (See [Media Flow protocol § Creating assets when missing](when-the-brief-is-branded.md#creating-assets-when-missing).)
|
|
159
159
|
|
|
160
160
|
## Bonus failure modes (BRICKS-specific, less common)
|
|
161
161
|
|
|
@@ -35,25 +35,31 @@ If you find yourself softening signature moves to "make it more accessible" —
|
|
|
35
35
|
3. **Build a 3-cell preview** — three minimal Subspaces (one Canvas each) the user can render. Don't produce finished work; the preview is a chooser.
|
|
36
36
|
4. **Once the user picks, drop out of Advisor mode** and continue the workflow rooted in that direction.
|
|
37
37
|
|
|
38
|
-
## Style declaration ritual
|
|
39
|
-
|
|
40
|
-
After the direction is picked,
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
## Style declaration ritual — into DESIGN.md
|
|
39
|
+
|
|
40
|
+
After the direction is picked, commit the system as named tokens in the project's [`DESIGN.md`](design-md.md) frontmatter (full schema there). Every subsequent choice cites a token by name; adding a value not already in the tokens requires adding the token first — that's what makes the system structural rather than aspirational.
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
system: Swiss Editorial (Pentagram lineage)
|
|
44
|
+
archetype: glance
|
|
45
|
+
colors:
|
|
46
|
+
ink: "#1A1A1A" # body text — never pure black
|
|
47
|
+
ground: "#F5F0E6" # default Canvas background
|
|
48
|
+
accent: "#FF3C00" # callouts only — one per Canvas
|
|
49
|
+
type: # sizes in grid units
|
|
50
|
+
display: { family: "<Brand Sans>", size: 16, weight: 400 }
|
|
51
|
+
heading: { family: "<Brand Sans>", size: 8, weight: 400 }
|
|
52
|
+
body: { family: "<Brand Sans>", size: 4, weight: 400 }
|
|
53
|
+
spacing: { gap-s: 2, gap-m: 4, gap-l: 8, margin: 8 } # inter-Brick gaps come only from here
|
|
54
|
+
grid: lean # strict alignment, generous margins
|
|
55
|
+
motion:
|
|
56
|
+
entrance: { ease: ease-out, ms: 280 }
|
|
57
|
+
exit: { ease: ease-in, ms: 180 }
|
|
58
|
+
loops: none
|
|
59
|
+
heroes: [brand-logo, folio-num] # persist across Canvases → auto-tween (Truth #3)
|
|
54
60
|
```
|
|
55
61
|
|
|
56
|
-
The grid itself is given by the runtime (Truth #6). What the
|
|
62
|
+
The grid itself is given by the runtime (Truth #6). What the tokens commit to is the set of design decisions on top of the grid: type scale, palette, **spacing scale in grid units** (small enumeration so inter-Brick gaps stay consistent across many siblings), grid stance, motion vocabulary, and hero Brick ids. DESIGN.md is not for the runtime; it is for *you*, when you next consider adding a third colour or a fourth gap value. Its existence is what holds the line.
|
|
57
63
|
|
|
58
64
|
---
|
|
59
65
|
|
|
@@ -262,4 +268,4 @@ When the user picks an awkward combination ("transact + Sagmeister"), surface th
|
|
|
262
268
|
|
|
263
269
|
The 10 directions are anchors. Real work often blends — "Kenya Hara emptiness with one Risograph spot-color accent for the call-to-action" is a legitimate combination if executed deliberately. What's not legitimate is blending three to dilute commitment.
|
|
264
270
|
|
|
265
|
-
When inventing a direction not in the library: write its system declaration
|
|
271
|
+
When inventing a direction not in the library: write its system declaration into DESIGN.md as if it were one of the entries above (pitch / flagship / keywords / fits / BRICKS execution / wrong-for). If you can't, the direction isn't real yet — keep working on it before committing.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# DESIGN.md — the project's design-system artifact
|
|
2
|
+
|
|
3
|
+
Every BRICKS design project commits its system to a single durable file: **`DESIGN.md`**, at the project root. It is the source of truth for type, palette, spacing, motion, brand assets, and deployment context — written once in Pass 0, cited by every Brick afterward, and read back at the top of every later session.
|
|
4
|
+
|
|
5
|
+
The format rests on four ideas:
|
|
6
|
+
|
|
7
|
+
1. **One named, durable markdown artifact** — not a system scattered across code comments that dies with the file. Markdown is the format an agent reads best; there is nothing to parse.
|
|
8
|
+
2. **Named tokens in YAML frontmatter, referenced by name in the prose.** Declare `accent` once; write `{colors.accent}` everywhere. The body never restates a raw hex or size — one place to change, no drift.
|
|
9
|
+
3. **Real design depth, not surface tokens.** Each token carries its *role* (which colour is CTA vs. body vs. hairline) and the system carries its *signature moves* (the one gesture that makes it recognisable) and the *why* behind each rule.
|
|
10
|
+
4. **An agent guide** that tells the next agent how to *use* the file.
|
|
11
|
+
|
|
12
|
+
The nine-section schema below is the web-design convention reframed for BRICKS. BRICKS is a fixed-frame, grid-substrate, state-machine runtime, so three of the nine canonical sections are reframed rather than copied — see the mapping table. Importing web habits (breakpoints, scroll, CSS elevation) through this file is the fastest way to fight the runtime.
|
|
13
|
+
|
|
14
|
+
## DESIGN.md replaces two older artifacts
|
|
15
|
+
|
|
16
|
+
This file is the merge point for what earlier versions of this skill kept apart:
|
|
17
|
+
|
|
18
|
+
- The **style-declaration comment block** at the top of the Subspace file → its tokens move into the DESIGN.md frontmatter. The Subspace comment shrinks to a one-line pointer (see [§ The Subspace pointer](#the-subspace-pointer)).
|
|
19
|
+
- **`brand-spec.md`** → its asset bindings, score log, and gaps become the **Assets & Brand** sections of DESIGN.md. One artifact, not two. (The acquisition *protocol* still lives in [`when-the-brief-is-branded.md`](when-the-brief-is-branded.md); DESIGN.md is where its output lands.)
|
|
20
|
+
|
|
21
|
+
A project has exactly one DESIGN.md. A multi-Subspace Application shares it; per-Subspace deviations are noted as scoped overrides inside it, never as a competing second file.
|
|
22
|
+
|
|
23
|
+
## The nine sections, mapped to BRICKS
|
|
24
|
+
|
|
25
|
+
| Web section | BRICKS section | Why it changes |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Visual Theme & Atmosphere | **1. Visual Theme & Atmosphere** | Unchanged in spirit — adds the picked visual language × interaction archetype. |
|
|
28
|
+
| Color Palette & Roles | **2. Colour Palette & Roles** | Colours used in 2+ places become theme-token Data bound via DataLink (Truth #2). |
|
|
29
|
+
| Typography Rules | **3. Typography** | Sizes are **grid units**, not px. Families are `ApplicationFont` entries with a fallback ladder. |
|
|
30
|
+
| Component Stylings | **4. Brick & Generator Stylings** | Bricks and Generators, not DOM components. Pressable affordance must read as visually distinct; Generators are headless/async (Truth #5). |
|
|
31
|
+
| Layout Principles | **5. Grid, Spacing & Rhythm** | Frames are grid units (Truth #6). No scroll, overflow, or reflow (Truth #10). |
|
|
32
|
+
| Depth & Elevation | **6. Motion, Standby & Depth** | No CSS z-index / box-shadow model. Depth = Brick draw order + Standby entrance offsets (Truth #7) + shared-id auto-tween (Truth #3). |
|
|
33
|
+
| Do's and Don'ts | **7. Do's & Don'ts** | Each rule tied to what it protects; folds in this system's anti-slop. |
|
|
34
|
+
| Responsive Behavior | **8. Hardware & Orientation** | **Not breakpoints.** Fixed target resolution + orientation; fleet variants are separate Canvas sets or device-scoped Data, never media queries. |
|
|
35
|
+
| Agent Prompt Guide | **9. Agent Guide** | How to cite tokens, add tokens, and verify against this file. |
|
|
36
|
+
|
|
37
|
+
Two BRICKS-native sections have no web equivalent and sit up front: **Deployment Context** (captures Priority #0 durably) and **Assets & Brand** (the merged `brand-spec.md`).
|
|
38
|
+
|
|
39
|
+
## Token-reference discipline — the load-bearing habit
|
|
40
|
+
|
|
41
|
+
This is what separates a system from an accumulation:
|
|
42
|
+
|
|
43
|
+
- **Declare every reusable value as a named token in the frontmatter.** Reference it by name in the prose and in your reasoning: `{colors.ink}`, `{type.display}`, `{spacing.gap-m}`, `{heroes.brand-logo}`.
|
|
44
|
+
- **Never restate a raw value in the body.** Writing `#1A1A1A` inline instead of `{colors.ink}` forks the source of truth; the next edit updates one and not the other.
|
|
45
|
+
- **Adding a value not already in the tokens requires adding the token first.** Want a third colour or a fourth gap? Edit the frontmatter, then use it. The "first" is the whole mechanism — it forces a deliberate pause where averaging-toward-generic would otherwise sneak a value in.
|
|
46
|
+
- **Sizes and gaps are in grid units (`gu`), never pixels.** The grid substrate is given by the runtime (Truth #6); the design sits on top of it.
|
|
47
|
+
- **Shared colours (2+ uses) become theme-token Data** bound via DataLink, with the token name matching the DESIGN.md key. Single-use decorative colours may stay hardcoded (Truth #2 is loose by design).
|
|
48
|
+
|
|
49
|
+
## The template
|
|
50
|
+
|
|
51
|
+
Copy this verbatim into `DESIGN.md` and fill it. Every section is required; an empty section is a declared gap, not an omission.
|
|
52
|
+
|
|
53
|
+
````markdown
|
|
54
|
+
---
|
|
55
|
+
name: <Application name> — design system
|
|
56
|
+
system: <visual language, e.g. Swiss Editorial (Pentagram lineage)>
|
|
57
|
+
archetype: <glance | browse | interact | transact | monitor | dwell>
|
|
58
|
+
|
|
59
|
+
deployment:
|
|
60
|
+
hardware: <size · resolution · orientation · touch? · viewing distance>
|
|
61
|
+
scene: <where the screen lives, what the user does in front of it>
|
|
62
|
+
network: <always-on | intermittent | offline>
|
|
63
|
+
languages: [<en>, <...>]
|
|
64
|
+
peripherals: [<camera | BLE | MQTT | NFC | payment | printer | sensors | none>]
|
|
65
|
+
watchdog: <fleet-managed | single-instance>
|
|
66
|
+
|
|
67
|
+
colors: # role in the comment; hex is the value
|
|
68
|
+
ink: "#1A1A1A" # body text — deep, never pure black
|
|
69
|
+
ground: "#F5F0E6" # default Canvas background
|
|
70
|
+
accent: "#FF3C00" # callouts only — at most one per Canvas
|
|
71
|
+
hairline: "#E3E0D6" # 1gu rules and card edges
|
|
72
|
+
# forbidden: [<colours the brand explicitly never uses>]
|
|
73
|
+
|
|
74
|
+
type: # sizes in grid units (gu)
|
|
75
|
+
display: { family: "<Brand Sans>", size: 16, weight: 300, tracking: -0.02 }
|
|
76
|
+
heading: { family: "<Brand Sans>", size: 8, weight: 400, tracking: 0 }
|
|
77
|
+
body: { family: "<Brand Sans>", size: 4, weight: 400, tracking: 0 }
|
|
78
|
+
caption: { family: "<Brand Sans>", size: 3, weight: 400, tracking: 0 }
|
|
79
|
+
|
|
80
|
+
spacing: # inter-Brick gaps come ONLY from this set (gu)
|
|
81
|
+
gap-s: 2
|
|
82
|
+
gap-m: 4
|
|
83
|
+
gap-l: 8
|
|
84
|
+
margin: 8
|
|
85
|
+
|
|
86
|
+
grid: lean # lean (strict align, generous margin) | break | breathe
|
|
87
|
+
|
|
88
|
+
motion: # Standby Transition vocabulary (Truth #7)
|
|
89
|
+
entrance: { standbyMode: "<offset, e.g. translateY 4gu>", ease: ease-out, ms: 280, stagger: 40 }
|
|
90
|
+
exit: { ease: ease-in, ms: 180 }
|
|
91
|
+
loops: <none | only on {brick-id}>
|
|
92
|
+
|
|
93
|
+
heroes: # Brick ids reused across Canvases → runtime auto-tweens them (Truth #3)
|
|
94
|
+
- brand-logo
|
|
95
|
+
- folio-num
|
|
96
|
+
|
|
97
|
+
fonts: # ApplicationFont entries → Application.applicationSettings.fonts
|
|
98
|
+
- { name: "<Brand Sans>", url: "<media-flow-or-cdn-url>", md5: "<optional>" }
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
# <Application name> — Design System
|
|
102
|
+
> Captured: <YYYY-MM-DD> · Sources: <reference pages / Figma / brand book> · Coverage: <complete | partial | inferred>
|
|
103
|
+
|
|
104
|
+
## Deployment Context
|
|
105
|
+
The five Priority #0 facts in prose, plus peripherals and watchdog. Every visual
|
|
106
|
+
decision below is downstream of these. If any of the first five was assumed rather
|
|
107
|
+
than confirmed, mark it **ASSUMED** here so the next agent knows what to re-check.
|
|
108
|
+
|
|
109
|
+
## 1. Visual Theme & Atmosphere
|
|
110
|
+
What the system *says* in two or three sentences. Name the picked **visual language**
|
|
111
|
+
and **interaction archetype**, then the **signature moves** — the one or two gestures
|
|
112
|
+
that make this system recognisable at a glance (e.g. "oversized folio numerals + a
|
|
113
|
+
single hairline rule per Canvas"). When unsure, commit *harder* to these, not softer.
|
|
114
|
+
|
|
115
|
+
**Key characteristics:** 4–6 bullets, each a concrete, checkable rule.
|
|
116
|
+
|
|
117
|
+
## 2. Colour Palette & Roles
|
|
118
|
+
Each token by name with its job. Reference `{colors.<name>}` — never the raw hex.
|
|
119
|
+
- `{colors.ink}` — body text on `{colors.ground}`. Deep charcoal, never pure black.
|
|
120
|
+
- `{colors.accent}` — callouts and the single emphasis per Canvas. Never a body colour.
|
|
121
|
+
- `{colors.hairline}` — 1gu rules, card edges.
|
|
122
|
+
- Forbidden: <colours the brand never uses>, if any.
|
|
123
|
+
Shared colours bind as theme-token Data; note which here.
|
|
124
|
+
|
|
125
|
+
## 3. Typography
|
|
126
|
+
- Families and the **fallback ladder** — what to substitute when the licensed font is
|
|
127
|
+
absent (e.g. "Söhne → Inter weight 300 + tracking -0.02"). Declared as `fonts` entries.
|
|
128
|
+
- Each role (`{type.display}` … `{type.caption}`) with size in gu, weight, tracking,
|
|
129
|
+
and *where it is used*. Multilingual note: which scripts the families cover.
|
|
130
|
+
|
|
131
|
+
## 4. Brick & Generator Stylings
|
|
132
|
+
- **Pressable affordance** — how a tile that responds to a press looks different from a
|
|
133
|
+
decorative one. A pressable that looks identical to décor is a bug. (Wiring lives in
|
|
134
|
+
`bricks-ux`; the *visual* distinction lives here.)
|
|
135
|
+
- **Hero Bricks** — the ids in `heroes`, what each is, and how it reads across Canvases.
|
|
136
|
+
- **Generator-driven components** — list / chart / feed surfaces fed by a headless,
|
|
137
|
+
async Generator (Truth #5). Note their loading, empty, and error appearance.
|
|
138
|
+
- **Media-bound Bricks** — every brand visual reads from Media Flow via DataLink (see
|
|
139
|
+
Assets & Brand); none is drawn, embedded, or set to a literal path.
|
|
140
|
+
|
|
141
|
+
## 5. Grid, Spacing & Rhythm
|
|
142
|
+
- **Grid stance** `{grid}` — lean / break / breathe, and what that means in margins.
|
|
143
|
+
- **Spacing scale** — inter-Brick gaps come only from `{spacing.*}`. Ten gaps of
|
|
144
|
+
7/9/11/13 across siblings is arithmetic drift; snap to the scale.
|
|
145
|
+
- **Density rhythm** — how consecutive Canvases vary (not three identical layouts).
|
|
146
|
+
- **No scroll, overflow, or reflow** (Truth #10). What does not fit does not show;
|
|
147
|
+
design within the Canvas at the target resolution.
|
|
148
|
+
|
|
149
|
+
## 6. Motion, Standby & Depth
|
|
150
|
+
- **Standby Transition** `{motion.entrance}` / `{motion.exit}` — every visible Brick
|
|
151
|
+
gets an entrance offset; the runtime auto-tweens in-flight motion (Truth #7).
|
|
152
|
+
- **Shared-id auto-tween** — heroes keep their id across Canvases so position/size
|
|
153
|
+
animate for free (Truth #3). This is the highest-leverage polish technique.
|
|
154
|
+
- **Depth** is Brick draw order, not a z/shadow model. State the stacking intent.
|
|
155
|
+
- **Snap-cuts** are reserved for emphasis (alarms), never routine navigation.
|
|
156
|
+
|
|
157
|
+
## 7. Do's & Don'ts
|
|
158
|
+
Each tied to what it protects. Examples:
|
|
159
|
+
- **Do** reserve `{colors.accent}` for one emphasis per Canvas — sparingness is the signal.
|
|
160
|
+
- **Don't** soften the signature moves "to be safe" — at 30% the language reads hesitant.
|
|
161
|
+
- **Don't** introduce a gap outside `{spacing.*}` — add the token first or don't add it.
|
|
162
|
+
|
|
163
|
+
## 8. Hardware & Orientation
|
|
164
|
+
Not breakpoints — BRICKS frames are fixed.
|
|
165
|
+
- **Target** resolution and orientation (from Deployment Context). The design is built
|
|
166
|
+
for this exact frame.
|
|
167
|
+
- **Viewing distance → minimum legible size** in gu, especially for `glance` signage.
|
|
168
|
+
- **Fleet variants** — if the same Application runs on differing resolutions, say how:
|
|
169
|
+
a separate Canvas set, or device-scoped Data — *not* a media query or reflow.
|
|
170
|
+
- **Language fit** — longest-string behaviour; the design must hold without overflow.
|
|
171
|
+
|
|
172
|
+
## 9. Agent Guide
|
|
173
|
+
- Reference tokens by name (`{colors.accent}`, `{spacing.gap-m}`); never inline a raw value.
|
|
174
|
+
- To add any value, add the token to the frontmatter first, then use it.
|
|
175
|
+
- Default body to `{type.body}`; default gap to `{spacing.gap-m}`.
|
|
176
|
+
- The Subspace's top comment points here; do not re-declare the system inline.
|
|
177
|
+
- Before declaring done, the design-critique pass cites this file section by section,
|
|
178
|
+
and every Canvas screenshot is compared against the tokens here.
|
|
179
|
+
|
|
180
|
+
## Assets & Brand
|
|
181
|
+
(Merged brand spec — populated via the protocol in `when-the-brief-is-branded.md`.)
|
|
182
|
+
|
|
183
|
+
### First-class assets
|
|
184
|
+
- **Logo** — light / dark / mono → Media Flow IDs → Data keys (`brandLogoDark` …),
|
|
185
|
+
clearspace, minimum size, source.
|
|
186
|
+
- **Product / UI / scene photography** (per brand type) → IDs → Data keys, each with
|
|
187
|
+
its 5-10-2-8 score `[R_ P_ B_ C_ N_ = _/10]`.
|
|
188
|
+
- **Motion** — brand reel / animated logo → IDs → Data keys.
|
|
189
|
+
|
|
190
|
+
### Voice & tone
|
|
191
|
+
Register, capitalization, banned phrases — a constraint on every user-visible Data string.
|
|
192
|
+
|
|
193
|
+
### Gaps & placeholders
|
|
194
|
+
`<category>: <why missing> → <ask user | generate-with-anchor | accept placeholder> [as of <date>]`
|
|
195
|
+
|
|
196
|
+
### Score log
|
|
197
|
+
`<asset>: R_ P_ B_ C_ N_ = _/10` for every non-logo asset.
|
|
198
|
+
````
|
|
199
|
+
|
|
200
|
+
## The Subspace pointer
|
|
201
|
+
|
|
202
|
+
The Subspace file no longer carries the full system as a comment. It carries a one-line pointer so a reader knows where the system lives and what it is at a glance:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
/** System: Swiss Editorial · archetype: glance · tokens + assets in ../DESIGN.md */
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
If a Subspace genuinely needs a scoped deviation (a darker ground for a single monitoring Subspace, say), note the override inside DESIGN.md under that Subspace's name and point the comment at it — never fork a second system silently.
|
|
209
|
+
|
|
210
|
+
## How DESIGN.md threads through the workflow
|
|
211
|
+
|
|
212
|
+
- **Pass 0** writes DESIGN.md: frontmatter tokens + Deployment Context + section 1 at minimum. ([`workflow.md`](workflow.md))
|
|
213
|
+
- **Picking a visual language** fills sections 1–3 and `system`/`archetype`. ([`design-languages.md`](design-languages.md))
|
|
214
|
+
- **Branded work** fills Assets & Brand and the colour/type/voice tokens. ([`when-the-brief-is-branded.md`](when-the-brief-is-branded.md))
|
|
215
|
+
- **Variations** are alternate token sets, expressed as a flat preset map keyed to these tokens, not a second DESIGN.md. ([`variations-and-tweaks.md`](variations-and-tweaks.md))
|
|
216
|
+
- **Verification & critique** read DESIGN.md back and check the rendered Canvases against it section by section. ([`design-critique.md`](design-critique.md))
|
|
217
|
+
|
|
218
|
+
## Anti-patterns specific to DESIGN.md
|
|
219
|
+
|
|
220
|
+
- **Inlining raw values in the body** instead of `{token}` references — forks the source of truth.
|
|
221
|
+
- **A breakpoint table** in section 8 — that is a web habit; BRICKS frames are fixed (Truth #10).
|
|
222
|
+
- **A CSS-style elevation model** in section 6 — depth is draw order + Standby, not z-index/shadow.
|
|
223
|
+
- **Two artifacts** — a `brand-spec.md` *and* a DESIGN.md, or per-Subspace system blocks competing with the file. One project, one DESIGN.md.
|
|
224
|
+
- **Writing DESIGN.md after the Canvases** — it is a Pass 0 commitment that the build cites, not a doc written to describe finished work.
|
|
225
|
+
- **Tokens with no roles** — `accent: "#FF3C00"` with no note on what it is for is a swatch, not a system. The role is the design depth.
|
|
@@ -20,13 +20,18 @@ How to inspect, by source type:
|
|
|
20
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
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
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. |
|
|
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. If the host only shows a cover preview or has no PDF capability, use an installed PDF skill when available. If no PDF skill or local PDF tool is installed and you cannot inspect the PDF fully, stop and respond in chat with the limitation instead of designing from partial evidence. |
|
|
24
24
|
| Local images / screenshots | Read each via the image tool. |
|
|
25
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
26
|
| Live competitor product / app | Run it (or have the user run it and capture screen recordings); inspect the screenshots/states. |
|
|
27
27
|
|
|
28
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
29
|
|
|
30
|
+
For PDF-derived page images, keep the artifacts sized for inspection. A giant
|
|
31
|
+
4-13 MB PNG per page is a tool failure waiting to happen, not a useful
|
|
32
|
+
handoff. Prefer downsampled JPEG/PNG previews, page batches, and a simple
|
|
33
|
+
coverage checklist (`seen pages: 1-8 of 8`) before design work starts.
|
|
34
|
+
|
|
30
35
|
Inspection comes before everything else in this file. The translation table below assumes you have already seen every relevant frame.
|
|
31
36
|
|
|
32
37
|
## A render is one state, not an Application
|
|
@@ -128,7 +133,7 @@ When the source contains brand binaries, you are inheriting them — but only at
|
|
|
128
133
|
- 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
134
|
- 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
135
|
|
|
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
|
|
136
|
+
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 under DESIGN.md's **Gaps & placeholders** 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
137
|
|
|
133
138
|
### Brand colors and tokens
|
|
134
139
|
|
|
@@ -136,6 +141,8 @@ Score everything against 5-10-2-8 before binding. If the source's brand binaries
|
|
|
136
141
|
- One-off decorative color → hardcode (Truth #2's loose convention).
|
|
137
142
|
- Token system from a brand book (light / dark / accent / semantic) → expose as Data tokens; switch theme by writing to the token Data values.
|
|
138
143
|
|
|
144
|
+
Extracted colors, type, and spacing become the named tokens in [`DESIGN.md`](design-md.md) (referenced by name thereafter), so the translated system is captured once rather than re-derived per Canvas.
|
|
145
|
+
|
|
139
146
|
### Long copy / paragraphs
|
|
140
147
|
|
|
141
148
|
- 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.
|
|
@@ -26,7 +26,7 @@ Use when variations differ only on values: palette, copy, asset paths, density f
|
|
|
26
26
|
**Shape:**
|
|
27
27
|
|
|
28
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
|
|
29
|
+
- Source has a `VARIATION_PRESETS` map near the top of the Subspace define file. Each preset is a flat map of the DESIGN.md token keys (`ground`, `ink`, `accent`, `gap-m`, …) — a variation is one alternate token set, not a second DESIGN.md. Keep the map flat — nested config trees make per-preset diffs noisy.
|
|
30
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
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
32
|
|
|
@@ -128,9 +128,9 @@ The bar exists because cosmetic-quality drift is invisible until shipped, then o
|
|
|
128
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
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
130
|
|
|
131
|
-
**Logging the scores**
|
|
131
|
+
**Logging the scores** under DESIGN.md's **Score log** (per category) is what makes the bar real. If you didn't log a score, you didn't apply the bar.
|
|
132
132
|
|
|
133
|
-
### Step 5 — Bind into Media Flow +
|
|
133
|
+
### Step 5 — Bind into Media Flow + record in DESIGN.md
|
|
134
134
|
|
|
135
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
136
|
|
|
@@ -145,17 +145,17 @@ Bricks bind via DataLink: `Image.property.source = linkData(() => brandLogoData)
|
|
|
145
145
|
|
|
146
146
|
Fonts: declare each as an `ApplicationFont` entry in `Application.applicationSettings.fonts` (`name`, `url`, optional `md5`). Reference by name in Brick `property.fontFamily`.
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
The brand spec is **not** a separate file — it is the **Assets & Brand** sections of the project's [`DESIGN.md`](design-md.md), alongside the colour/type/voice tokens. Use this structure verbatim under DESIGN.md's `## Assets & Brand` heading — every section is required, even the gap section:
|
|
149
149
|
|
|
150
150
|
```markdown
|
|
151
|
-
|
|
151
|
+
## Assets & Brand
|
|
152
152
|
> Captured: <YYYY-MM-DD>
|
|
153
153
|
> Sources: <list>
|
|
154
154
|
> Coverage: <complete / partial / inferred>
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
### First-class assets
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
#### Logo
|
|
159
159
|
- Primary (dark ground): <Media Flow ID> → Data: brandLogoDark
|
|
160
160
|
- Primary (light ground): <Media Flow ID> → Data: brandLogoLight
|
|
161
161
|
- Mono / reverse: <Media Flow ID> → Data: brandLogoMono
|
|
@@ -163,57 +163,56 @@ Fonts: declare each as an `ApplicationFont` entry in `Application.applicationSet
|
|
|
163
163
|
- Minimum size: <e.g., 96 px wide>
|
|
164
164
|
- Source: <press-kit URL / inline-svg from <url> / file from user>
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
#### Product photography (if physical brand)
|
|
167
167
|
- Hero front: <Media Flow ID> → Data: productHero (3000×2000, transparent) [score: R9 P9 B8 C9 N9]
|
|
168
168
|
- Detail crop A: ... [score: ...]
|
|
169
169
|
- Scene context: ... [score: ...]
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
#### UI screenshots (if digital brand)
|
|
172
172
|
- Home: ... [score: ...]
|
|
173
173
|
- Core feature A: ... [score: ...]
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
#### Scene / lifestyle (if relevant)
|
|
176
176
|
- ...
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
#### Motion
|
|
179
179
|
- Brand reel clip: <Media Flow ID> → Data: brandReel (1920×1080, 12s, h264)
|
|
180
180
|
- Animated logo: <Media Flow ID> → Data: brandLogoLottie
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
### Auxiliary
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
- Forbidden: <colors the brand explicitly does not use>
|
|
184
|
+
#### Colours
|
|
185
|
+
The palette lives in the DESIGN.md frontmatter `colors` tokens (shared ones bound as theme-token Data). Record provenance and constraints here:
|
|
186
|
+
- `{colors.primary}` / `{colors.accent}` / neutrals — source: <where each was sampled / documented>
|
|
187
|
+
- Forbidden: <colours the brand explicitly does not use>
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
- Display: <family / weights / file location / license>
|
|
192
|
-
- Body: <family / weights / file location / license>
|
|
189
|
+
#### Fonts (ApplicationFont entries)
|
|
190
|
+
- Display: <family / weights / file location / license> → frontmatter `type.display` + `fonts`
|
|
191
|
+
- Body: <family / weights / file location / license> → frontmatter `type.body` + `fonts`
|
|
193
192
|
|
|
194
|
-
|
|
193
|
+
#### Voice & tone
|
|
195
194
|
- Register: <e.g., confident, terse, never effusive>
|
|
196
195
|
- Capitalization: <e.g., sentence case>
|
|
197
196
|
- Banned phrases: <e.g., "innovative", "revolutionary", "AI-powered">
|
|
198
197
|
|
|
199
|
-
|
|
198
|
+
#### Signature details
|
|
200
199
|
- <One or two design moves the brand is known for — a specific corner radius, a hairline rule, a particular typographic treatment.>
|
|
201
200
|
|
|
202
|
-
|
|
201
|
+
### Gaps & placeholders
|
|
203
202
|
- <category>: <why missing> → <action: ask user / AI generate / accept placeholder> [as of <date>]
|
|
204
203
|
|
|
205
|
-
|
|
204
|
+
### Score log (for non-logo assets)
|
|
206
205
|
- <asset>: R<n> P<n> B<n> C<n> N<n> = <avg>/10
|
|
207
206
|
```
|
|
208
207
|
|
|
209
208
|
## Structural enforcement — assets are referenced, not redrawn
|
|
210
209
|
|
|
211
|
-
Once
|
|
210
|
+
Once DESIGN.md's Assets & Brand sections are populated, the discipline that keeps the design honest is structural, not behavioural. Encode it in the build:
|
|
212
211
|
|
|
213
212
|
- **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
213
|
- **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
214
|
- **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
|
|
215
|
+
- **Every Brick that uses a brand asset** has its source traceable to a DESIGN.md Assets & Brand line. Reviewing DESIGN.md is reviewing the design.
|
|
217
216
|
|
|
218
217
|
## Creating assets when missing
|
|
219
218
|
|
|
@@ -221,13 +220,13 @@ When a category has no acquirable real asset and the user has no plan to provide
|
|
|
221
220
|
|
|
222
221
|
1. **Stop and ask.** For logo, this is the only allowed option.
|
|
223
222
|
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
|
|
223
|
+
3. **Accept a labeled placeholder Brick** with the gap tracked under DESIGN.md's **Gaps & placeholders**. 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
224
|
|
|
226
225
|
### Generative discipline (when option 2)
|
|
227
226
|
|
|
228
227
|
- **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
228
|
- **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**
|
|
229
|
+
- **Persist generation metadata** under DESIGN.md's Assets & Brand: prompt, seed (where supported), reference assets used, model version. Reproducibility matters when the user later asks for a refresh in the same style.
|
|
231
230
|
- **Never generate logos, wordmarks, or named characters.** These are identity, not imagery — generation produces look-alikes that legal will reject.
|
|
232
231
|
- **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
232
|
- **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.
|
|
@@ -255,7 +254,7 @@ Some BRICKS deployments are for venues with no prior brand work — small clinic
|
|
|
255
254
|
|
|
256
255
|
- Skip the protocol; admit the gap up front.
|
|
257
256
|
- 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
|
|
257
|
+
- 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 in DESIGN.md (tokens + Assets & Brand) — 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
258
|
|
|
260
259
|
## Voice & tone as a constraint on user-visible Data
|
|
261
260
|
|
|
@@ -274,11 +273,11 @@ You cannot move from this protocol back into design until all of the following a
|
|
|
274
273
|
1. Logo — present in Media Flow with light + dark + mono where applicable; bound via Data.
|
|
275
274
|
2. Product / UI / scene category appropriate to the brand type — present, scored ≥ 8/10 on each kept asset, bound via Data.
|
|
276
275
|
3. Motion category — present where the brand has motion identity; absent and noted otherwise.
|
|
277
|
-
4. Colors — palette declared as
|
|
276
|
+
4. Colors — palette declared as DESIGN.md `colors` tokens (shared ones bound as theme-token Data).
|
|
278
277
|
5. Fonts — declared as `ApplicationFont` entries; license confirmed.
|
|
279
278
|
6. Voice & tone — captured as constraint or marked TBD with stakeholder + date.
|
|
280
279
|
7. Gaps — every missing or placeholder asset listed under "Gaps & placeholders" with action and date.
|
|
281
280
|
8. Score log — every non-logo asset scored on the 5 dimensions and logged.
|
|
282
|
-
9. Spec committed —
|
|
281
|
+
9. Spec committed — DESIGN.md's Assets & Brand sections are filled in the project root; not in a chat scrollback or a working file that will be lost.
|
|
283
282
|
|
|
284
283
|
If any item is incomplete, name it in your reply and either resolve it or pause for user input. Don't proceed silently.
|
|
@@ -61,31 +61,27 @@ When you do ask, cover these axes — in one batch, omitting any already pinned:
|
|
|
61
61
|
|
|
62
62
|
Don't sprint to a finished Subspace. Stage the work so wrong direction is caught at the cheapest point.
|
|
63
63
|
|
|
64
|
-
### Pass 0 — Deployment context +
|
|
64
|
+
### Pass 0 — Deployment context + DESIGN.md
|
|
65
65
|
|
|
66
|
-
Confirm Priority #0 verbatim
|
|
66
|
+
Confirm Priority #0 verbatim, then write the project's **`DESIGN.md`** before placing any Brick. Full schema and template in [`design-md.md`](design-md.md); at Pass 0 you need at minimum:
|
|
67
|
+
|
|
68
|
+
- **Frontmatter tokens** — `system`, `archetype`, the `colors` / `type` (sizes in grid units) / `spacing` (grid units) / `motion` / `grid` / `heroes` maps. Each token carries its role in a comment.
|
|
69
|
+
- **Deployment Context** — the five Priority #0 facts in prose, with any assumed value flagged `ASSUMED`.
|
|
70
|
+
- **Visual Theme & Atmosphere** (section 1) — what the system says and its one or two signature moves.
|
|
71
|
+
|
|
72
|
+
The grid substrate is given (Truth #6). What you commit to is type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, and hero Bricks. The spacing scale is a small enumeration (3–5 values); inter-Brick gaps come from this set by name (`{spacing.gap-m}`), not arbitrary integers. DESIGN.md keeps the agent's arithmetic consistent across many Bricks and is what the design-critique pass cites.
|
|
73
|
+
|
|
74
|
+
The Subspace file itself carries only a one-line pointer:
|
|
67
75
|
|
|
68
76
|
```ts
|
|
69
|
-
/**
|
|
70
|
-
* System: <picked design language, e.g., Swiss Editorial>
|
|
71
|
-
* Archetype: <interaction archetype, e.g., glance>
|
|
72
|
-
* Type: <font family> · <size scale in grid units, e.g., 2 / 4 / 8 / 16 gu>
|
|
73
|
-
* Color: <ground> on <ink> · accent <#hex> (used only for …)
|
|
74
|
-
* Spacing: <scale in grid units, e.g., 2 / 4 / 8 / 16 gu>
|
|
75
|
-
* Grid stance: lean / break / breathe
|
|
76
|
-
* Motion: <Standby easing + ms> entrance · <ms> exit · loops only on <element>
|
|
77
|
-
* Heroes: <Brick id(s) that persist across Canvases via auto-tween>
|
|
78
|
-
* Asset emphasis: <real photography / type-only / hero video / …>
|
|
79
|
-
*/
|
|
77
|
+
/** System: <picked design language> · archetype: <archetype> · tokens + assets in ../DESIGN.md */
|
|
80
78
|
```
|
|
81
79
|
|
|
82
|
-
The grid substrate is given (Truth #6). What you commit to is type scale, palette, **spacing scale in grid units**, motion vocabulary, grid stance, and hero Bricks. The spacing scale is a small enumeration (3–5 values); inter-Brick gaps come from this set, not arbitrary integers. This block keeps the agent's arithmetic consistent across many Bricks and is what the design-critique pass cites.
|
|
83
|
-
|
|
84
80
|
### Pass 1 — Showcase Canvas (the lockdown moment)
|
|
85
81
|
|
|
86
82
|
Build **one** Canvas, fully lit, that represents the work at its highest fidelity. Choose the hardest Canvas — the boot screen, the most content-dense state, or the moment that carries the brand most. Run through Verification (compile + screenshot + view back). Show the user.
|
|
87
83
|
|
|
88
|
-
This is the cheapest point to discover the direction is wrong. If the user wants the type bigger, the photography swapped, the rhythm slower, the language different — fix the Canvas and the
|
|
84
|
+
This is the cheapest point to discover the direction is wrong. If the user wants the type bigger, the photography swapped, the rhythm slower, the language different — fix the Canvas and the DESIGN.md tokens, not twelve Canvases.
|
|
89
85
|
|
|
90
86
|
**Do not** build the full Canvas graph before this checkpoint. The temptation is real because state-machine work feels productive; resist it. Sign-off on one Canvas first.
|
|
91
87
|
|
|
@@ -131,4 +127,4 @@ Don't compress passes on:
|
|
|
131
127
|
- **Asking one question at a time.** Burns volleys, drains user energy.
|
|
132
128
|
- **Asking nothing and inferring.** The five Priority #0 items are not inferrable; trying to is the most expensive mistake.
|
|
133
129
|
- **Declaring done after Pass 2.** Pass 4 critique is what separates "runs" from "good."
|
|
134
|
-
- **
|
|
130
|
+
- **Rewriting DESIGN.md tokens on every iteration.** If you find yourself updating five tokens at once mid-Pass-3, you're rebuilding the system — back up to Pass 1.
|
|
@@ -2,6 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
import { sh } from '../_shell'
|
|
5
|
+
import { appendSimulatorInspectionGuidance } from './simulator-guidance'
|
|
5
6
|
|
|
6
7
|
// Disable ANSI color codes from spawned tools so MCP text output stays readable
|
|
7
8
|
// (the host renders raw text; escape sequences leak into the result otherwise).
|
|
@@ -78,6 +79,7 @@ export function register(server: McpServer, projectDir: string) {
|
|
|
78
79
|
log = [stdout, stderr].filter(Boolean).join('\n')
|
|
79
80
|
error = true
|
|
80
81
|
}
|
|
82
|
+
if (!error) log = appendSimulatorInspectionGuidance(log, { testId, testTitleLike })
|
|
81
83
|
let screenshotBase64: string | null = null
|
|
82
84
|
if (!error && responseImage) {
|
|
83
85
|
const toolsDir = `${dirname}/..`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const quoteShellArg = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`
|
|
2
|
+
|
|
3
|
+
const buildInteractiveSimulatorCommand = ({
|
|
4
|
+
testId,
|
|
5
|
+
testTitleLike,
|
|
6
|
+
}: {
|
|
7
|
+
testId?: string
|
|
8
|
+
testTitleLike?: string
|
|
9
|
+
} = {}) => {
|
|
10
|
+
const args = ['bun preview']
|
|
11
|
+
if (testId) args.push('--test-id', quoteShellArg(testId))
|
|
12
|
+
if (testTitleLike) args.push('--test-title-like', quoteShellArg(testTitleLike))
|
|
13
|
+
return args.join(' ')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function appendSimulatorInspectionGuidance(
|
|
17
|
+
log: string,
|
|
18
|
+
options: { testId?: string; testTitleLike?: string } = {},
|
|
19
|
+
) {
|
|
20
|
+
return [
|
|
21
|
+
log.trimEnd(),
|
|
22
|
+
'',
|
|
23
|
+
'Inspection guidance:',
|
|
24
|
+
`- For interactive debugging of this same Preview runtime, run \`${buildInteractiveSimulatorCommand(options)}\` from the project directory. It exposes CDP on localhost:19852 by default and writes .bricks/devtools.json for CLI discovery.`,
|
|
25
|
+
'- Use `bricks devtools -p 19852 ...` from the project directory to inspect the Simulator. Plain `bricks devtools` may inspect another device on the default port.',
|
|
26
|
+
'- Useful commands: `bricks devtools -p 19852 brick tree`, `brick query <selector>`, `input tap <x> <y>`, `network list`, `storage data-bank get <S_xxxx>`, and `runtime eval "automation.list()" -j`.',
|
|
27
|
+
'- Prefer this Preview CDP/runtime path over a separate Playwright browser when validating BRICKS app state, logs, input, storage, or network behavior.',
|
|
28
|
+
'- If the `bricks-cli` skill is unavailable, run `bricks devtools --help`; the CLI help is authoritative.',
|
|
29
|
+
'',
|
|
30
|
+
].join('\n')
|
|
31
|
+
}
|
package/tools/pull.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readdir, readFile, unlink, writeFile } from 'node:fs/promises'
|
|
1
|
+
import { mkdir, readdir, readFile, unlink, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
-
import { join, relative } from 'node:path'
|
|
3
|
+
import { dirname, join, relative } from 'node:path'
|
|
4
4
|
import { format } from 'oxfmt'
|
|
5
5
|
import { sh } from './_shell'
|
|
6
6
|
import { extractCliErrorMessage } from './_cli-error'
|
|
@@ -86,7 +86,11 @@ const branchName = isModule
|
|
|
86
86
|
? 'BRICKS_PROJECT_try-pull-module'
|
|
87
87
|
: 'BRICKS_PROJECT_try-pull-application'
|
|
88
88
|
|
|
89
|
+
let landingBranch = ''
|
|
89
90
|
if (isGitRepo && !force) {
|
|
91
|
+
landingBranch = (await sh`cd ${cwd} && git branch --show-current`.text()).trim()
|
|
92
|
+
if (!landingBranch) throw new Error('Cannot pull from a detached HEAD')
|
|
93
|
+
|
|
90
94
|
console.log(`Checking commit ${baseCommitId}...`)
|
|
91
95
|
const found = (await sh`cd ${cwd} && git rev-list -1 ${baseCommitId}`.nothrow().text())
|
|
92
96
|
.trim()
|
|
@@ -100,8 +104,8 @@ if (isGitRepo && !force) {
|
|
|
100
104
|
|
|
101
105
|
// When the base commit isn't reachable in this clone (server stored a
|
|
102
106
|
// nanoid, or the commit was pruned), fall back to forking from current
|
|
103
|
-
// HEAD. The downstream merge into
|
|
104
|
-
// same result, just with different merge bases.
|
|
107
|
+
// HEAD. The downstream merge into the starting branch collapses both paths
|
|
108
|
+
// into the same result, just with different merge bases.
|
|
105
109
|
if (found) {
|
|
106
110
|
await sh`cd ${cwd} && git checkout -b ${branchName} ${baseCommitId}`.nothrow()
|
|
107
111
|
} else {
|
|
@@ -143,7 +147,9 @@ await Promise.all(
|
|
|
143
147
|
const result = await format(file.name, file.input, oxfmtConfig)
|
|
144
148
|
content = result.code
|
|
145
149
|
}
|
|
146
|
-
|
|
150
|
+
const target = join(cwd, file.name)
|
|
151
|
+
await mkdir(dirname(target), { recursive: true })
|
|
152
|
+
return writeFile(target, content)
|
|
147
153
|
}),
|
|
148
154
|
)
|
|
149
155
|
|
|
@@ -160,11 +166,11 @@ if (isGitRepo) {
|
|
|
160
166
|
await sh`cd ${cwd} && git ${commitArgs}`
|
|
161
167
|
}
|
|
162
168
|
if (!force) {
|
|
163
|
-
// Land the pulled commits on
|
|
169
|
+
// Land the pulled commits on the starting branch with a single 3-way merge using
|
|
164
170
|
// baseCommit as the merge base. The user doesn't have to manage a side
|
|
165
|
-
// branch, and conflicts (if any) land in the working tree on
|
|
171
|
+
// branch, and conflicts (if any) land in the working tree on the starting branch where
|
|
166
172
|
// auto-compile surfaces them as typecheck errors to resolve in-place.
|
|
167
|
-
await sh`cd ${cwd} && git checkout
|
|
173
|
+
await sh`cd ${cwd} && git checkout ${landingBranch}`
|
|
168
174
|
const mergeResult = await sh`cd ${cwd} && git merge ${branchName} --no-edit`.nothrow()
|
|
169
175
|
if (mergeResult.exitCode !== 0) {
|
|
170
176
|
// Conflict markers are in the working tree — commit them so the tree
|
|
@@ -174,7 +180,7 @@ if (isGitRepo) {
|
|
|
174
180
|
await sh`cd ${cwd} && git add .`
|
|
175
181
|
const conflictArgs = await buildCommitArgs(
|
|
176
182
|
cwd,
|
|
177
|
-
[
|
|
183
|
+
[`chore(project): merge with conflicts (resolve in ${landingBranch})`],
|
|
178
184
|
['--no-verify'],
|
|
179
185
|
)
|
|
180
186
|
await sh`cd ${cwd} && git ${conflictArgs}`
|
package/types/bricks/Items.d.ts
CHANGED
|
@@ -269,6 +269,7 @@ Default property:
|
|
|
269
269
|
y?: number | DataLink
|
|
270
270
|
width?: number | DataLink
|
|
271
271
|
height?: number | DataLink
|
|
272
|
+
type?: string | DataLink
|
|
272
273
|
standbyMode?: 'custom' | 'top' | 'bottom' | 'left' | 'right' | DataLink
|
|
273
274
|
standbyFrame?: DataLink | {}
|
|
274
275
|
standbyOpacity?: number | DataLink
|
|
@@ -319,6 +320,7 @@ Default property:
|
|
|
319
320
|
}
|
|
320
321
|
show?: string | DataLink
|
|
321
322
|
pressToOpenDetail?: boolean | DataLink
|
|
323
|
+
pressToBackList?: boolean | DataLink
|
|
322
324
|
}
|
|
323
325
|
>
|
|
324
326
|
| DataLink
|
|
@@ -346,6 +348,7 @@ Default property:
|
|
|
346
348
|
y?: number | DataLink
|
|
347
349
|
width?: number | DataLink
|
|
348
350
|
height?: number | DataLink
|
|
351
|
+
type?: string | DataLink
|
|
349
352
|
standbyMode?: 'custom' | 'top' | 'bottom' | 'left' | 'right' | DataLink
|
|
350
353
|
standbyFrame?: DataLink | {}
|
|
351
354
|
standbyOpacity?: number | DataLink
|
|
@@ -395,6 +398,7 @@ Default property:
|
|
|
395
398
|
renderOutOfViewport?: boolean | DataLink
|
|
396
399
|
}
|
|
397
400
|
show?: string | DataLink
|
|
401
|
+
pressToOpenDetail?: boolean | DataLink
|
|
398
402
|
pressToBackList?: boolean | DataLink
|
|
399
403
|
}
|
|
400
404
|
>
|
package/types/bricks/Sketch.d.ts
CHANGED
|
@@ -191,9 +191,11 @@ Default property:
|
|
|
191
191
|
/* A stroke was just committed (drawn or added programmatically) */
|
|
192
192
|
onStrokeEnd?: Array<EventAction<string & keyof TemplateEventPropsMap['Sketch']['onStrokeEnd']>>
|
|
193
193
|
/* The canvas was cleared */
|
|
194
|
-
onClear?: Array<EventAction
|
|
194
|
+
onClear?: Array<EventAction<string & keyof TemplateEventPropsMap['Sketch']['onClear']>>
|
|
195
195
|
/* Sketch state changed (any commit, undo, redo, clear, or import) */
|
|
196
|
-
onStateChange?: Array<
|
|
196
|
+
onStateChange?: Array<
|
|
197
|
+
EventAction<string & keyof TemplateEventPropsMap['Sketch']['onStateChange']>
|
|
198
|
+
>
|
|
197
199
|
/* Active tool changed */
|
|
198
200
|
onToolChange?: Array<
|
|
199
201
|
EventAction<string & keyof TemplateEventPropsMap['Sketch']['onToolChange']>
|
package/types/bricks/Video.d.ts
CHANGED
|
@@ -63,7 +63,13 @@ Default property:
|
|
|
63
63
|
"muted": false,
|
|
64
64
|
"progressUpdateInterval": 1000,
|
|
65
65
|
"replayTimeout": 500,
|
|
66
|
-
"renderMode": "auto"
|
|
66
|
+
"renderMode": "auto",
|
|
67
|
+
"showSubtitles": true,
|
|
68
|
+
"subtitleColor": "#FFFFFF",
|
|
69
|
+
"subtitleBackgroundColor": "rgba(0, 0, 0, 0.6)",
|
|
70
|
+
"subtitleFontSize": 2,
|
|
71
|
+
"subtitlePosition": "bottom",
|
|
72
|
+
"subtitleTextAlign": "center"
|
|
67
73
|
}
|
|
68
74
|
*/
|
|
69
75
|
property?: BrickBasicProperty & {
|
|
@@ -106,6 +112,57 @@ Default property:
|
|
|
106
112
|
replayTimeout?: number | DataLink
|
|
107
113
|
/* Use what view type for render video (Auto / Texture or Surface). Notice: Although using surface has better performance, it also affects the Animation & Standby Transition used by itself */
|
|
108
114
|
renderMode?: 'auto' | 'texture' | 'surface' | DataLink
|
|
115
|
+
/* The subtitle file path list or single path (SRT/WebVTT), index-matched to the video path. Used when no inline subtitles are set for that video. */
|
|
116
|
+
subtitlePath?:
|
|
117
|
+
| string
|
|
118
|
+
| DataLink
|
|
119
|
+
| DataLink
|
|
120
|
+
| {
|
|
121
|
+
url?: string | DataLink
|
|
122
|
+
}
|
|
123
|
+
| Array<
|
|
124
|
+
| DataLink
|
|
125
|
+
| {
|
|
126
|
+
url?: string | DataLink
|
|
127
|
+
}
|
|
128
|
+
| string
|
|
129
|
+
| DataLink
|
|
130
|
+
| DataLink
|
|
131
|
+
>
|
|
132
|
+
| DataLink
|
|
133
|
+
| DataLink
|
|
134
|
+
/* Inline subtitles per video, index-matched to the video path: each entry holds a `cues` list of { start, end, text } (start/end in seconds). Takes precedence over the subtitle file for that video. */
|
|
135
|
+
subtitles?:
|
|
136
|
+
| Array<
|
|
137
|
+
| DataLink
|
|
138
|
+
| {
|
|
139
|
+
cues?:
|
|
140
|
+
| Array<
|
|
141
|
+
| DataLink
|
|
142
|
+
| {
|
|
143
|
+
start?: number | DataLink
|
|
144
|
+
end?: number | DataLink
|
|
145
|
+
text?: string | DataLink
|
|
146
|
+
}
|
|
147
|
+
>
|
|
148
|
+
| DataLink
|
|
149
|
+
}
|
|
150
|
+
>
|
|
151
|
+
| DataLink
|
|
152
|
+
/* Render the subtitle overlay. Turn off to only trigger subtitle events without rendering text */
|
|
153
|
+
showSubtitles?: boolean | DataLink
|
|
154
|
+
/* Subtitle text color */
|
|
155
|
+
subtitleColor?: string | DataLink
|
|
156
|
+
/* Subtitle background color */
|
|
157
|
+
subtitleBackgroundColor?: string | DataLink
|
|
158
|
+
/* Subtitle font size, in grid units (same as Brick Text) */
|
|
159
|
+
subtitleFontSize?: number | DataLink
|
|
160
|
+
/* Subtitle font family */
|
|
161
|
+
subtitleFontFamily?: string | DataLink
|
|
162
|
+
/* Subtitle vertical position */
|
|
163
|
+
subtitlePosition?: 'bottom' | 'top' | DataLink
|
|
164
|
+
/* Subtitle text alignment */
|
|
165
|
+
subtitleTextAlign?: 'left' | 'center' | 'right' | DataLink
|
|
109
166
|
}
|
|
110
167
|
events?: BrickBasicEvents & {
|
|
111
168
|
/* Event of the brick press */
|
|
@@ -130,6 +187,14 @@ Default property:
|
|
|
130
187
|
onError?: Array<EventAction>
|
|
131
188
|
/* Event of the video progress interval */
|
|
132
189
|
onProgress?: Array<EventAction<string & keyof TemplateEventPropsMap['Video']['onProgress']>>
|
|
190
|
+
/* Event when a subtitle cue starts (appears) during playback */
|
|
191
|
+
subtitleCueEnter?: Array<
|
|
192
|
+
EventAction<string & keyof TemplateEventPropsMap['Video']['subtitleCueEnter']>
|
|
193
|
+
>
|
|
194
|
+
/* Event when a subtitle cue ends (disappears) during playback */
|
|
195
|
+
subtitleCueExit?: Array<
|
|
196
|
+
EventAction<string & keyof TemplateEventPropsMap['Video']['subtitleCueExit']>
|
|
197
|
+
>
|
|
133
198
|
}
|
|
134
199
|
outlets?: {
|
|
135
200
|
/* Brick is pressing */
|
|
@@ -149,6 +214,8 @@ Default property:
|
|
|
149
214
|
onLoad?: AnimationOrGetter
|
|
150
215
|
onError?: AnimationOrGetter
|
|
151
216
|
onProgress?: AnimationOrGetter
|
|
217
|
+
subtitleCueEnter?: AnimationOrGetter
|
|
218
|
+
subtitleCueExit?: AnimationOrGetter
|
|
152
219
|
}
|
|
153
220
|
}
|
|
154
221
|
|
|
@@ -157,7 +157,12 @@ Default property:
|
|
|
157
157
|
/* Event name to listen on SSE */
|
|
158
158
|
eventName?: string | DataLink | Array<string | DataLink> | DataLink | DataLink
|
|
159
159
|
}
|
|
160
|
-
events?: {
|
|
160
|
+
events?: {
|
|
161
|
+
/* Event triggered when the HTTP request receives a response */
|
|
162
|
+
onResponse?: Array<EventAction<string & keyof TemplateEventPropsMap['Http']['onResponse']>>
|
|
163
|
+
/* Event triggered when the HTTP request or SSE stream reports an error */
|
|
164
|
+
onError?: Array<EventAction<string & keyof TemplateEventPropsMap['Http']['onError']>>
|
|
165
|
+
}
|
|
161
166
|
outlets?: {
|
|
162
167
|
/* Response for HTTP request */
|
|
163
168
|
response?: () => Data<string | { [key: string]: any }>
|
|
@@ -33,7 +33,7 @@ Default property:
|
|
|
33
33
|
property?: {
|
|
34
34
|
/* Start tick on generator initialized */
|
|
35
35
|
init?: boolean | DataLink
|
|
36
|
-
/*
|
|
36
|
+
/* Tick interval in milliseconds for countdown */
|
|
37
37
|
interval?: number | DataLink
|
|
38
38
|
/* Initial value of countdown */
|
|
39
39
|
countdownStartValue?: number | DataLink
|
package/utils/data.ts
CHANGED
package/utils/event-props.ts
CHANGED
|
@@ -16,6 +16,13 @@ export const templateEventPropsMap = {
|
|
|
16
16
|
Video: {
|
|
17
17
|
nextVideo: { BRICK_VIDEO_NEXT_INDEX: 'number' },
|
|
18
18
|
onProgress: { BRICK_VIDEO_CURRENT_TIME: 'number', BRICK_VIDEO_PLAYABLE_DURATION: 'number' },
|
|
19
|
+
subtitleCueEnter: {
|
|
20
|
+
BRICK_VIDEO_SUBTITLE_TEXT: 'string',
|
|
21
|
+
BRICK_VIDEO_SUBTITLE_INDEX: 'number',
|
|
22
|
+
BRICK_VIDEO_SUBTITLE_START: 'number',
|
|
23
|
+
BRICK_VIDEO_SUBTITLE_END: 'number',
|
|
24
|
+
},
|
|
25
|
+
subtitleCueExit: { BRICK_VIDEO_SUBTITLE_TEXT: 'string', BRICK_VIDEO_SUBTITLE_INDEX: 'number' },
|
|
19
26
|
},
|
|
20
27
|
VideoStreaming: {},
|
|
21
28
|
Qrcode: {},
|
|
@@ -142,7 +149,9 @@ export const templateEventPropsMap = {
|
|
|
142
149
|
},
|
|
143
150
|
},
|
|
144
151
|
Sketch: {
|
|
145
|
-
onStrokeEnd: { BRICK_SKETCH_STROKE_COUNT: 'number' },
|
|
152
|
+
onStrokeEnd: { BRICK_SKETCH_STROKE_COUNT: 'number', BRICK_SKETCH_STATE: '{}' },
|
|
153
|
+
onClear: { BRICK_SKETCH_STATE: '{}' },
|
|
154
|
+
onStateChange: { BRICK_SKETCH_STATE: '{}' },
|
|
146
155
|
onToolChange: { BRICK_SKETCH_TOOL: 'string' },
|
|
147
156
|
onExportImage: { BRICK_SKETCH_IMAGE_URI: 'string' },
|
|
148
157
|
},
|
|
@@ -267,7 +276,14 @@ export const templateEventPropsMap = {
|
|
|
267
276
|
subscribeDataOnConnectError: { GENERATOR_DATA_BANK_ERROR: '{ [key: string]: any }' },
|
|
268
277
|
},
|
|
269
278
|
Graphql: { subscriptionOnConnectionError: { GENERATOR_GRAPHQL_ERROR: '{ [key: string]: any }' } },
|
|
270
|
-
Http: {
|
|
279
|
+
Http: {
|
|
280
|
+
onResponse: {
|
|
281
|
+
GENERATOR_HTTP_RESPONSE: 'any',
|
|
282
|
+
GENERATOR_HTTP_RESPONSE_DETAILS:
|
|
283
|
+
'{ ok?: boolean redirected?: boolean type?: string status?: number statusText?: string headers?: { [key: string]: any } body?: any event?: string id?: string data?: any [key: string]: any }',
|
|
284
|
+
},
|
|
285
|
+
onError: { GENERATOR_HTTP_ERROR: 'string' },
|
|
286
|
+
},
|
|
271
287
|
SoundPlayer: { onLoadError: { GENERATOR_SOUND_PLAYER_ERROR: 'string' } },
|
|
272
288
|
Keyboard: {
|
|
273
289
|
onDown: {
|