0022. Brick Work leaves Toni's World and takes the App Design Standard layout; the screenshot gate compares the built app with approved captures
- Status: accepted
- Date: 2026-11-04
- Decided by: the maintainer, 4 Nov 2026 via the wiki-agent ("brick work should plan it in milestone B"; the four questions answered with the session's recommendations). Supersedes the launch-kit decision of 2026-10-15 that Brick Work stays inside Toni's World
Context
The App Design Standard (ai-wiki app-design-standard.md, phase 4.3) asks every app for its own neutral base and tokens, the P2 layout under design/, and a gate: every approved screen covered by a screenshot test, a token drift check, every moved path still served, and rendered tests that never skip silently. Brick Work still had the old layout: a Toni's World sub-brand skill, two token copies that have diverged, 770 flat frames, no screenshot tests and no drift check. Its check.sh skipped the Node harnesses silently.
Options considered
- Screenshots against the design frames (pixel-equal). The build was never pixel-equal to the frames, and a frame for a shipped feature lists its differences (#136).
- Screenshots against approved captures of the built app per screen state, with the frames kept as the design record.
- Token values from the design copy (the export's
design/sources/tokens-v2.css) or from the served copy (static/web/toni/tokens-v2.css).
Decision
- Option 2 for the screenshot gate. The frames stay the design record.
- The tokens take today's served look: no visual change in the roll-out. The design copy's differences go to the maintainer as a list.
- Rendering runs in CI too (Chrome installed in
check.yml), and a rendered test that can't run fails unlessBW_SKIP_RENDERED=1is set and named. CLAUDE.md's rule "do not edit files indesign/" becomes "design/sources/is the design project's export;design/is organised per P2 by scripts".- Planned as #158–#161 (D1–D4) in milestone B, with a line in gate #96.
Consequences
- The neutral base (design builder
base/tokens.css) loads before Brick Work's layer. It only adds custom properties Brick Work doesn't use yet (checked 4 Nov), so no computed value changes. - Served
/static/brickwork/...files become generated fromdesign/, and old URLs keep answering. - B's close-out moves after D3/D4.
State after D2 (#159)
The P2 layout is in place:
design/sources/holds the design project's export: the*.dc.htmlpages,frames.json(unchanged, byte for byte),index.html,styles.css,support.js,assets/,test-data/and the export's owntokens-v2.css. The kit's brand panel and specimen pages moved there from the skill; their token link now points at the file beside them.design/screens/NN-<screen>/has one folder perframes.jsonsection (57): the PNGs asNN-<Screen>[-<state>]-<width>-<light|dark>.pngand the section's static HTML.design/screens/manifest.jsonis generated byscripts/design_screens.py; itscheckis inscripts/check.sh. 44 iPad frames of an earlier 10a section, listed by no section, wait indesign/screens/_unmapped/. Amended 2026-11-05 (maintainer: "Retire them"): they are retired todesign/screens/_retired/, and the brand panel and specimen pages, which came from the design skill rather than the design export, moved fromdesign/sources/todesign/components/. The app's build captures aredesign/screens/approval/.design/logo/,design/app-icon/anddesign/components/icons/hold the brand sources. The served/static/brickwork/...files stay at their paths and are generated byte for byte byscripts/brand_assets.py build; itscheckis inscripts/check.sh.brick-work.cssandmanifest.webmanifestare the app's own and stay instatic/brickwork/.tests/test_brand_assets.pyasserts every URL served before D2 still answers with the same bytes, and that path-like parameters never reach a file. No handler with a path argument was added.design/DESIGN-CHECK.md, the skill (which points atdesign/and holds no copy) andCLAUDE.mdname the new paths.
Left for later: re-rendering the icon and splash PNG sizes from the SVG sources, one preview page per component in design/components/, and a rename of the 10a frames into a section if the design project adds one.
State after D3 (#160)
- Rendered tests never skip silently. A test that needs node or Chrome fails when the tool is missing.
BW_SKIP_RENDERED=1turns that into a skip;tests/conftest.pyrecords every such skip andscripts/check.shends with a "Skipped rendered tests" block naming them (zero says so).check.shalso fails its Node harness step when node is missing, unless the variable is set. The three tests that were opt-in behindBW_COLLECTION_CHROME(test_collection_screens,test_shell_49f,test_bw_planning) now always run; the variable only points them at a Chrome binary. Two had gone stale unseen and were brought up to date: the Collection geometry test expects the coverage captions that came with M.8e, and the planning geometry test no longer measures the Prices screen, whose rows the browser builds since #24 (covered bytoni_bw_prices_runtime.cjs). - The browser.
scripts/chrome.py:CHROME, else the pinned Chrome for Testing 157.0.8103.38 in~/.cache/brickwork/chrome/<version>/(downloaded on first use, SHA-256 checked, linux64 and mac-arm64), else a system Chrome. No npm packages. - Render in CI.
check.ymlcaches that directory and runscheck.shas before; CI never setsBW_SKIP_RENDERED. - The gate.
design/screens/states.json(hand-kept) says how each of the 57 sections is reached in the built app and whether it isbuiltandapproved;scripts/screen_gate.pymakes the candidates and compares the approved states (tests/test_screen_gate.py);design_screens.py checkkeeps the file anddesign/screens/approval/baselines/consistent. Everyapprovedis false: 33 states are built and await the maintainer's review, 28 are not built or not reached yet (each says why instates.json). A built state that is not approved has no baseline file in the repo; an approved state has all six. The gate reports 0 differ trivially until the first approval. - Baselines are made where the gate runs. Review images for the maintainer may be rendered anywhere. At approval the session runs the
baselinesworkflow (baselines.yml,workflow_dispatch) on ubuntu-latest for the approved screens, downloads the artifact and commits those PNGs with theAPPROVALS.mdline andapproved: true, in one commit. The gate compares in CI on Linux; a local macOScomparediffers slightly and is not authoritative: there the gate test prints its differences as advisory and passes, and only a Linux run fails on them (BW_GATE_STRICT=1forces it elsewhere; changed 2026-11-05, when the first 33 states were approved and every local macOS gate failed). A failing CI run uploadsbuild/screen-diffs/as thescreen-diffsartifact.
Left for later: the first approval (the maintainer's, through a review artifact); scripted interaction for the states that need it (edit sheet, filter panel, checker steps, sheets); fixtures for the states the demo dataset cannot reach (orders, lots, instructions and boxes, a watched price, several currencies).
State after #268
The gate has three worlds. demo is the dataset of the approved baselines and is never extended. showcase (the exported demo's invented collection) and extras (the showcase plus brickwork/demo/gate_extras.py) hold the data the later states need: orders, a lot, copies, staged checks, an instruction, boxes and a dollar wish. 39 more states are built as candidates (the maintainer approves them); a page another state already shows is recorded with covered_by, and the price watch (45) and the barcode scanner (57) are not in the app, so they stay unbuilt with that reason.
