@fugood/bricks-ctor 2.25.0-beta.5 → 2.25.0-beta.51
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/__tests__/config-diff.test.js +100 -0
- package/compile/__tests__/index.test.js +386 -0
- package/compile/__tests__/util.test.js +337 -0
- package/compile/action-name-map.ts +64 -0
- package/compile/config-diff.ts +155 -0
- package/compile/index.ts +278 -34
- package/compile/util.ts +34 -10
- package/package.json +7 -3
- package/skills/bricks-ctor/SKILL.md +23 -17
- package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
- package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +18 -0
- package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
- package/skills/bricks-ctor/references/buttress.md +245 -0
- package/skills/bricks-ctor/references/data-calculation.md +239 -0
- package/skills/bricks-ctor/references/simulator.md +132 -0
- package/skills/bricks-ctor/references/source-editing-tools.md +81 -0
- package/skills/bricks-ctor/references/verification-toolchain.md +200 -0
- package/skills/bricks-design/SKILL.md +150 -45
- package/skills/bricks-design/references/architecture-truths.md +132 -0
- package/skills/bricks-design/references/avoiding-complexity.md +91 -0
- package/skills/bricks-design/references/design-critique.md +195 -0
- package/skills/bricks-design/references/design-languages.md +265 -0
- package/skills/bricks-design/references/performance.md +116 -0
- package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
- package/skills/bricks-design/references/translating-inputs.md +152 -0
- package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
- package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
- package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
- package/skills/bricks-design/references/workflow.md +134 -0
- package/skills/bricks-ux/SKILL.md +114 -0
- package/skills/bricks-ux/references/accessibility.md +162 -0
- package/skills/bricks-ux/references/flow-states.md +175 -0
- package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
- package/skills/bricks-ux/references/monitoring-screens.md +153 -0
- package/skills/bricks-ux/references/pressable-composition.md +126 -0
- package/skills/bricks-ux/references/user-journey.md +168 -0
- package/skills/bricks-ux/references/ux-critique.md +256 -0
- package/tools/__tests__/_cli-error.test.ts +35 -0
- package/tools/__tests__/_mcp-config.test.ts +67 -0
- package/tools/_cli-error.ts +17 -0
- package/tools/_edits-log.ts +41 -0
- package/tools/_git-author.ts +10 -2
- package/tools/_last-pushed-commit.ts +28 -0
- package/tools/_mcp-config.ts +42 -0
- package/tools/_shell.ts +8 -1
- package/tools/deploy.ts +17 -6
- package/tools/mcp-env.ts +13 -0
- package/tools/mcp-server.ts +8 -0
- package/tools/mcp-tools/__tests__/data-calc-editing.test.js +516 -0
- package/tools/mcp-tools/__tests__/entry-editing.test.js +866 -0
- package/tools/mcp-tools/__tests__/huggingface.test.ts +49 -0
- package/tools/mcp-tools/__tests__/icons.test.ts +21 -0
- package/tools/mcp-tools/__tests__/mcp-env.test.js +19 -0
- package/tools/mcp-tools/_editing-helpers.ts +58 -0
- package/tools/mcp-tools/_verify.ts +50 -0
- package/tools/mcp-tools/compile.ts +21 -9
- package/tools/mcp-tools/data-calc-editing.ts +1349 -0
- package/tools/mcp-tools/entry-editing.ts +2336 -0
- package/tools/mcp-tools/huggingface.ts +23 -13
- package/tools/mcp-tools/icons.ts +23 -7
- package/tools/mcp-tools/media.ts +4 -1
- package/tools/postinstall.ts +95 -38
- package/tools/pull.ts +93 -22
- package/tools/push-config.ts +114 -0
- package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
- package/tools/simulator-preload.cjs +16 -0
- package/tools/{preview.ts → simulator.ts} +4 -4
- package/types/{animation.ts → animation.d.ts} +24 -8
- package/types/{automation.ts → automation.d.ts} +16 -20
- package/types/{brick-base.ts → brick-base.d.ts} +1 -1
- package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
- package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
- package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
- package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
- package/types/bricks/{Image.ts → Image.d.ts} +21 -9
- package/types/bricks/{Items.ts → Items.d.ts} +7 -7
- package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
- package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
- package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
- package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
- package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
- package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
- package/types/bricks/Scene3D.d.ts +676 -0
- package/types/bricks/{Sketch.ts → Sketch.d.ts} +6 -6
- package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
- package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
- package/types/bricks/{Text.ts → Text.d.ts} +9 -9
- package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
- package/types/bricks/{Video.ts → Video.d.ts} +12 -12
- package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
- package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
- package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
- package/types/bricks/{index.ts → index.d.ts} +1 -0
- package/types/{common.ts → common.d.ts} +3 -6
- package/types/data-calc-command/base.d.ts +57 -0
- package/types/data-calc-command/collection.d.ts +418 -0
- package/types/data-calc-command/color.d.ts +432 -0
- package/types/data-calc-command/constant.d.ts +50 -0
- package/types/data-calc-command/datetime.d.ts +147 -0
- package/types/data-calc-command/file.d.ts +129 -0
- package/types/data-calc-command/index.d.ts +13 -0
- package/types/data-calc-command/iteratee.d.ts +23 -0
- package/types/data-calc-command/logictype.d.ts +190 -0
- package/types/data-calc-command/math.d.ts +275 -0
- package/types/data-calc-command/object.d.ts +119 -0
- package/types/data-calc-command/sandbox.d.ts +66 -0
- package/types/data-calc-command/string.d.ts +407 -0
- package/types/{data-calc.ts → data-calc.d.ts} +1 -0
- package/types/{data.ts → data.d.ts} +4 -2
- package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
- package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
- package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
- package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
- package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
- package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
- package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
- package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
- package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
- package/types/{subspace.ts → subspace.d.ts} +1 -1
- package/utils/__tests__/calc.test.js +25 -0
- package/utils/__tests__/id.test.js +154 -0
- package/utils/calc.ts +5 -1
- package/utils/data.ts +5 -7
- package/utils/event-props.ts +17 -0
- package/utils/id.ts +109 -56
- package/skills/bricks-ctor/rules/buttress.md +0 -156
- package/skills/bricks-ctor/rules/data-calculation.md +0 -209
- package/skills/bricks-design/LICENSE.txt +0 -180
- package/types/data-calc-command.ts +0 -7005
- /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/media-flow.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
- /package/types/{canvas.ts → canvas.d.ts} +0 -0
- /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
- /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
- /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
- /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
- /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
- /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
- /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
- /package/types/generators/{File.ts → File.d.ts} +0 -0
- /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
- /package/types/generators/{Http.ts → Http.d.ts} +0 -0
- /package/types/generators/{HttpServer.ts → HttpServer.d.ts} +0 -0
- /package/types/generators/{Information.ts → Information.d.ts} +0 -0
- /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
- /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
- /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
- /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
- /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
- /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
- /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
- /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
- /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
- /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
- /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
- /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
- /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
- /package/types/generators/{Question.ts → Question.d.ts} +0 -0
- /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
- /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
- /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
- /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
- /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
- /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
- /package/types/generators/{Step.ts → Step.d.ts} +0 -0
- /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
- /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
- /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
- /package/types/generators/{Tick.ts → Tick.d.ts} +0 -0
- /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
- /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
- /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
- /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
- /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
- /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
- /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
- /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
- /package/types/generators/{index.ts → index.d.ts} +0 -0
- /package/types/{index.ts → index.d.ts} +0 -0
- /package/types/{switch.ts → switch.d.ts} +0 -0
- /package/types/{system.ts → system.d.ts} +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Simulator
|
|
2
|
+
|
|
3
|
+
What the Simulator can and can't faithfully reproduce — so a passing Simulator run means what you think it means. This is the fidelity companion to Path 1 in [Verification Toolchain](verification-toolchain.md); read that for how to *drive* the Simulator (compile, screenshot, Automations).
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
- The default Path 1 target: it renders your compiled project on your computer, with no device required.
|
|
8
|
+
- It runs in Chromium/Electron — a browser-class runtime, not the device runtime. Two consequences to design verification around: it has a browser's capabilities and limits (below), and its JavaScript engine differs from the device's (iOS runs JSC, Android runs Hermes), so engine-specific behavior won't surface here.
|
|
9
|
+
|
|
10
|
+
A Simulator screenshot proves **layout, navigation, Standby Transitions, Data wiring, and generator/Automation wiring**. It does *not* prove anything that depends on real hardware, real models, or capabilities the preview runtime lacks.
|
|
11
|
+
|
|
12
|
+
## Automation screenshots
|
|
13
|
+
|
|
14
|
+
`match_screenshot` stores Simulator automation artifacts at the project level:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
.bricks/automation_screenshots/
|
|
18
|
+
last/
|
|
19
|
+
failed/
|
|
20
|
+
diff/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
There is no application-id subdirectory; in CTOR the project is the application. Generated project `.gitignore` files exclude this directory.
|
|
24
|
+
|
|
25
|
+
When a `match_screenshot` baseline is missing or updated, the baseline goes under `last/`. When a screenshot mismatch occurs, the current capture goes under `failed/` and the pixel diff goes under `diff/`. File names follow `<screenshotName>+local+<timestamp>.png`.
|
|
26
|
+
|
|
27
|
+
In CTOR Desktop, `simulator_invoke` reports any automation screenshot files created or changed by the invocation in an `Automation screenshots:` section. It still separately reports the ordinary preview capture at `.bricks/simulator-screenshot.jpg`.
|
|
28
|
+
|
|
29
|
+
## What the Simulator can't reproduce
|
|
30
|
+
|
|
31
|
+
Because it runs in a browser-class preview rather than on device hardware, the Simulator cannot exercise these — a green run says nothing about them. Verify on a real device (Path 2):
|
|
32
|
+
|
|
33
|
+
| Area | In the Simulator |
|
|
34
|
+
|------|------------------|
|
|
35
|
+
| BLE (Central / Peripheral), Video Streaming | Not available |
|
|
36
|
+
| Raw network sockets — UDP, TCP, TCP Server, MQTT Broker | Not available (also why Buttress LAN discovery is a no-op — see [Buttress](buttress.md)) |
|
|
37
|
+
| Android Intent | Not available (Android-only) |
|
|
38
|
+
| On-device database **persistence** (SQLite / Vector Store) | Not available — both run in-memory in the Simulator (see caveats below), so data never survives a reload |
|
|
39
|
+
| GGML Text-to-Speech | No vocoder in the preview — use an ONNX TTS model to hear speech |
|
|
40
|
+
|
|
41
|
+
These work, but with browser caveats:
|
|
42
|
+
|
|
43
|
+
| Area | Caveat |
|
|
44
|
+
|------|--------|
|
|
45
|
+
| Serial Port | Works via WebSerial — requires browser support and a user gesture |
|
|
46
|
+
| WebView / WebCrawler | Subject to browser CORS — a load/fetch that works on device may be blocked |
|
|
47
|
+
| On-device AI (LLM / STT / VAD / Vector Store / Reranker) | Runs **single-threaded** — far slower than the device, not representative of real latency. Also subject to the model fallbacks below |
|
|
48
|
+
| On-device database (SQLite — `GENERATOR_SQLITE`) | Runs for real on the in-memory WASM `sqlite-vec` build — `execute` / `query` / `transaction` / batch all work. `storageType: file` is transparently treated as in-memory, so nothing persists across reloads (see above) |
|
|
49
|
+
| Scene3D / Sketch / WebRTC | Supported |
|
|
50
|
+
|
|
51
|
+
Feature availability also varies across the device platforms themselves (iOS / tvOS / Android / the desktop OSes). When a deployment targets a specific platform's capability, confirm it on that platform.
|
|
52
|
+
|
|
53
|
+
## Network security
|
|
54
|
+
|
|
55
|
+
The Simulator blocks insecure connections from `applicationPreview` by default: `http://` and `ws://` requests fail, while `https://` and `wss://` are allowed. The Simulator internally allows its own preview host when running the development preview.
|
|
56
|
+
|
|
57
|
+
Generated projects include this project-level setting:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"allowedInsecureHosts": []
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Add explicit entries only when you deliberately need to test an insecure endpoint in the Simulator. A bare `host:port` allows both `http://` and `ws://` for that host:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"allowedInsecureHosts": ["localhost:8080"]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use a URL form when only one insecure protocol should be allowed:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"allowedInsecureHosts": ["http://localhost:8080"]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
In that example, `http://localhost:8080` is allowed and `ws://localhost:8080` remains blocked.
|
|
82
|
+
|
|
83
|
+
Prefer HTTPS/WSS for anything that is not local development.
|
|
84
|
+
|
|
85
|
+
## Feature fallbacks — what the Simulator fakes
|
|
86
|
+
|
|
87
|
+
So that camera and AI features are usable without device permissions, multi-gigabyte downloads, API keys, or a remote inference backend, the Simulator **transparently substitutes a lightweight stand-in** for a few bricks/generators. The config you author is untouched — only the Simulator's runtime behavior differs. **A fallback render proves wiring and layout, not the real feature.**
|
|
88
|
+
|
|
89
|
+
| Brick / Generator | In the Simulator | Does NOT prove |
|
|
90
|
+
|-------------------|------------------|----------------|
|
|
91
|
+
| Camera (`BRICK_CAMERA`) | A 3D mock canvas, no camera permission prompt. `takePicture` snapshots the canvas; recording produces a placeholder clip | Real camera feed, focus, recording, permission flow |
|
|
92
|
+
| Maps (`BRICK_MAPS`) | A real interactive map on free OpenStreetMap-based tiles — no Google Maps API key needed. Markers, path polyline, the six themes / map types (approximated with free tile sets + CSS tints), and the zoom / pan / navigate / focus / reset / fit actions all work | Google / Apple Maps rendering, exact `customMapStyle` / theme styling (approximated), traffic / buildings / indoors layers, real device geolocation |
|
|
93
|
+
| Thermal Printer (`GENERATOR_THERMAL_PRINTER`) | A simulated printer — `init` / `checkStatus` / `scan` fake per-driver status and discovered devices (ESC/POS, Star, TSC, Castles); `print` renders an approximate on-screen receipt. A bottom-left bubble shows live status with a fault toggle to exercise error wiring. Print results can be exported as PNG via `bricks-cli` (see below) | Real device connection, actual paper output, exact native driver status codes |
|
|
94
|
+
| LLM (`GENERATOR_LLM`) | Swapped to a tiny local stand-in model | Output quality / latency of your real model |
|
|
95
|
+
| Reranker — GGML (`GENERATOR_RERANKER`) | Swapped to a small local multilingual reranker model | Ranking quality / latency of your real model |
|
|
96
|
+
| Speech-to-Text — GGML (`GENERATOR_SPEECH_INFERENCE`) | Swapped to a tiny local model | Accuracy / latency of your real model |
|
|
97
|
+
| Speech-to-Text — ONNX (`GENERATOR_ONNX_STT`) | Swapped to a tiny whisper model | Accuracy / latency of your real model |
|
|
98
|
+
| Text-to-Speech — ONNX (`GENERATOR_TTS`) | Swapped to a tiny VITS model (no vocoder or speaker embedding) | Voice / quality / latency of your real model |
|
|
99
|
+
| Vector Store (`GENERATOR_VECTOR_STORE`) | Runs with a tiny local embedding model — an OpenAI-source config works **without a key** | Real embeddings / dimensions / recall (and no persistence — see above) |
|
|
100
|
+
| Buttress (all generators) | **Disabled** — there is no remote inference backend in the Simulator | The Buttress offload path — see [Buttress](buttress.md) |
|
|
101
|
+
| MLX LLM (`GENERATOR_MLX_LLM`) | Runs as authored, except Buttress is disabled | The MLX / Buttress path |
|
|
102
|
+
|
|
103
|
+
These swaps make the AI generators runnable in the Simulator for wiring checks — validate real models, quality, and latency on a device (Path 2).
|
|
104
|
+
|
|
105
|
+
### Inspecting simulated thermal printer print results
|
|
106
|
+
|
|
107
|
+
With the Simulator running (CDP at `localhost:19852`, see [Verification Toolchain](verification-toolchain.md)), `bricks-cli` can read the simulated printers and export a print result as a PNG image — no need to open the bubble popover or screenshot the whole window:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Printers + print history (newest first; shows the --index value per result)
|
|
111
|
+
bricks devtools simulator thermal-printer list -p 19852
|
|
112
|
+
bricks devtools simulator thermal-printer list -p 19852 -j --commands # full print payloads as JSON
|
|
113
|
+
|
|
114
|
+
# Render one print result to PNG (defaults: the only printer, --index 0 = latest)
|
|
115
|
+
bricks devtools simulator thermal-printer print-result -p 19852 -o receipt.png
|
|
116
|
+
bricks devtools simulator thermal-printer print-result -p 19852 --index 1 --scale 2 -o older.png
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The PNG is the same approximate receipt the on-screen preview shows (rendered from the print payload). It proves the payload wiring — content, ordering, alignment, barcode/QR/image presence — not pixel-exact native raster output. Print history is in-memory (up to 10 per printer) and clears on preview reload.
|
|
120
|
+
|
|
121
|
+
### Running the real implementation instead
|
|
122
|
+
|
|
123
|
+
Each substituted brick/generator can be switched back to its real implementation per item: open the **gear (Simulator settings)** in the editor's preview toolbar, uncheck the item, and **Apply**. Apply persists the choice and reloads the preview so it takes effect (a plain refresh won't). Use this to, e.g., point a Vector Store at a real API key in the preview, or render the real Google/Apple Maps brick (which needs a Maps API key on web). The browser limits above still apply, and **Buttress stays disabled regardless** — there's no backend for it here.
|
|
124
|
+
|
|
125
|
+
The Thermal Printer is the exception: it has no real web implementation to switch to (the native drivers can't run in a browser), so it is **always simulated** and is not in the gear list.
|
|
126
|
+
|
|
127
|
+
## Enough vs. escalate
|
|
128
|
+
|
|
129
|
+
- **Simulator is enough for:** layout, navigation, Standby Transitions, Data wiring, Automation / state-machine happy paths, and confirming a generator fires and handles its events.
|
|
130
|
+
- **Escalate to a real device for:** anything in the "can't reproduce" tables, real camera / peripherals / payment / identity, real model quality & latency, multi-device Local Sync, on-device persistence, the Buttress offload path, and engine-specific behavior.
|
|
131
|
+
|
|
132
|
+
The full Path 1/2/3 decision rule lives in [Verification Toolchain](verification-toolchain.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# MCP Source-Editing Tools
|
|
2
|
+
|
|
3
|
+
The project-local `bricks-ctor` MCP server exposes tools that edit `subspaces/**` via
|
|
4
|
+
surgical AST edits, keeping files in the standard generated style. Prefer them over
|
|
5
|
+
hand-editing entry files; they validate references, manage imports, compile-verify, and
|
|
6
|
+
record every operation in `.bricks/edits.jsonl`.
|
|
7
|
+
|
|
8
|
+
## Entry tools (bricks.ts / generators.ts / canvases.ts / data.ts / animations.ts)
|
|
9
|
+
|
|
10
|
+
| Tool | Purpose |
|
|
11
|
+
|------|---------|
|
|
12
|
+
| `new_entry` | Create a standard entry skeleton (`file`, `type`, `templateKey`, `alias`, optional initial `set`/`events`) |
|
|
13
|
+
| `edit_entry` | Set/unset dotted paths on an entry: `title`, `property.url`, `outlets.response`, `value`, `switches[0].property.text`, … |
|
|
14
|
+
| `edit_events` | Add/remove/replace/move/clear EventAction items in `events.<eventKey>` |
|
|
15
|
+
| `edit_canvas_items` | Add/replace/remove/move brick items on a Canvas `items` array (frames need numeric x/y/width/height) |
|
|
16
|
+
| `edit_switches` | Add/replace/remove/move switches (id, title, conds, override, disabled, break) |
|
|
17
|
+
| `remove_entry` | Delete an entry; cascades same-subspace references by default, `strict: true` refuses and lists sites |
|
|
18
|
+
|
|
19
|
+
Addressing: `{ file, entry }` (export const name) is primary; `id` works as a global
|
|
20
|
+
fallback (omit `file`). `edit_entry`/`edit_events` accept a `switch` parameter (switch id
|
|
21
|
+
or index) to edit the facets inside one switch — `edit_switches` owns only the array and
|
|
22
|
+
the conds/override shell.
|
|
23
|
+
|
|
24
|
+
### Event actions
|
|
25
|
+
|
|
26
|
+
```jsonc
|
|
27
|
+
{
|
|
28
|
+
"handler": "system", // or { "ref": "brickOrGeneratorRef" } or { "subspace": "SUBSPACE_id" }
|
|
29
|
+
"name": "CHANGE_CANVAS",
|
|
30
|
+
"params": { "canvasId": { "ref": "mainCanvas" } }, // template params, OR:
|
|
31
|
+
"dataParams": { "someData": 5 }, // PROPERTY_BANK-style params
|
|
32
|
+
"waitAsync": false
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
System actions derive their `as SystemAction...` cast automatically; entity-action casts
|
|
37
|
+
are added only when passed via `cast`. The compiled source form
|
|
38
|
+
`{ handler, action: { name, params: [...], dataParams: [...] }, waitAsync }` is also
|
|
39
|
+
accepted, and `{ "expr": "<raw EventAction>" }` is the escape hatch for shapes the
|
|
40
|
+
structured form cannot express. Handler refs must resolve to a brick or generator.
|
|
41
|
+
`PROPERTY_BANK_EXPRESSION` expressions are validated against the runtime fold rules at
|
|
42
|
+
edit time: only expression statements, simple `const`/`let` declarations, and a final
|
|
43
|
+
return inside a zero-arg IIFE evaluate — no `if`/`for`/`while` (use ternaries, or a
|
|
44
|
+
DataCalculationScript for branching logic).
|
|
45
|
+
|
|
46
|
+
## Data-calc tools (DataCalculationScript only)
|
|
47
|
+
|
|
48
|
+
| Tool | Purpose |
|
|
49
|
+
|------|---------|
|
|
50
|
+
| `new_data_calc` | Create `data-calc/data-calculation-{slug}.ts` + `.sandbox.js`, regenerate `data-calc/index.ts`, wire a minimal subspace root |
|
|
51
|
+
| `edit_data_calc` | Set/unset scalar fields, `output`/`error` refs, replace whole `inputs`/`outputs`, or rewrite the sandbox `code` |
|
|
52
|
+
| `edit_data_calc_io` | Add/remove/replace/clear single `inputs`/`outputs` items (input keys unique; output keys may repeat for fan-out) |
|
|
53
|
+
| `remove_data_calc` | Delete the calc `.ts` + its sandbox file and regenerate the index |
|
|
54
|
+
|
|
55
|
+
Addressing: `{ file }` or `{ subspace, calc }` where `calc` is alias, id, or filename
|
|
56
|
+
slug (subspace defaults to `subspace-0`). Code is canonicalized to the sandbox file form
|
|
57
|
+
(`export function main() { ... }` — wrapped automatically, `async` added when the body
|
|
58
|
+
uses top-level `await`). `DataCalculationMap` (visual node graph) is out of scope and
|
|
59
|
+
returns `fallback_recommended`.
|
|
60
|
+
|
|
61
|
+
## Value grammar (everywhere a value appears)
|
|
62
|
+
|
|
63
|
+
| You pass | Emits |
|
|
64
|
+
|----------|-------|
|
|
65
|
+
| JSON scalar/array/object | literal |
|
|
66
|
+
| `{ "link": "dataRefOrAlias" }` | `linkData(() => data.dX)` (property data-links) |
|
|
67
|
+
| `{ "ref": "idOrAliasOrVarName", "subspace"?: 1 }` | `() => namespace.varName` getter |
|
|
68
|
+
| `{ "expr": "raw TypeScript" }` | spliced verbatim |
|
|
69
|
+
|
|
70
|
+
References resolve by var name, id, or alias within the target subspace and fail loudly
|
|
71
|
+
when missing or ambiguous — this doubles as reference validation.
|
|
72
|
+
|
|
73
|
+
## Verification and audit
|
|
74
|
+
|
|
75
|
+
- Every call compile-verifies by default and returns `verify.configChange` — the minimal
|
|
76
|
+
compiled-config delta (path-keyed set/unset ops). Skip with per-call `verify: false`
|
|
77
|
+
or `BRICKS_CTOR_MCP_EDIT_VERIFY=0` when batching, then finish with the `compile` tool.
|
|
78
|
+
- All operations append to `.bricks/edits.jsonl` (gitignored) with inputs, outcomes, and
|
|
79
|
+
touched sites.
|
|
80
|
+
- Non-standard files or entries (hand-written shapes the AST editor cannot safely
|
|
81
|
+
rewrite) return `fallback_recommended` — use plain file edits for those cases only.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Verification Toolchain
|
|
2
|
+
|
|
3
|
+
Three runtime targets, one decision rule. Verify against the cheapest path that proves what you need to prove.
|
|
4
|
+
|
|
5
|
+
## Definition of done — the hard gate
|
|
6
|
+
|
|
7
|
+
Before the agent is allowed to claim work is complete, **all** of the following must be true:
|
|
8
|
+
|
|
9
|
+
1. **Compile clean.** Latest source passes `compile` with no errors.
|
|
10
|
+
2. **Every Canvas screenshot captured.** A rendered screenshot of every Canvas in the deliverable, captured via the available preview tool (`responseImage: true` when the selected model supports vision) at the target hardware resolution and orientation. Canvases gated behind events are reached via Automation runs (`testId` / `testTitleLike` with `brick_press` / `wait_until_canvas_change` cases). Single-Canvas Subspaces still require a captured screenshot.
|
|
11
|
+
3. **Every screenshot reviewed by the agent.** The agent must read each captured screenshot back through the host's image-reading capability — saving the file is not the same as seeing it. The model that produced the Canvas must also have seen the rendered result, or the verification gate has not passed.
|
|
12
|
+
4. **Reference comparison report.** If the user supplied any reference material (Figma / website / HTML / screenshot / PDF / brand book / competitor), produce a short delta report per Canvas:
|
|
13
|
+
- What matches the reference.
|
|
14
|
+
- What intentionally diverges, and why (deployment constraint, BRICKS-runtime semantic, system commitment).
|
|
15
|
+
- What unintentionally diverges, and the planned fix.
|
|
16
|
+
5. **Path appropriate to deployment executed.** Path 1 by default. Path 2 (on-device) when the deployment depends on real hardware behaviour, or whenever the brief touches payment, identity, peripherals, or LocalSync.
|
|
17
|
+
6. **Console clean.** No 404s on media, no Data-key-undefined warnings, no Generator init failures, no React-style warnings — every console line is either zero or accept-with-a-comment.
|
|
18
|
+
|
|
19
|
+
What does **not** count as done:
|
|
20
|
+
|
|
21
|
+
- A single hero-Canvas screenshot.
|
|
22
|
+
- "Looks roughly like the reference" with no per-Canvas comparison.
|
|
23
|
+
- "Compiled successfully" with no rendered preview.
|
|
24
|
+
- A claim of done with no screenshots in evidence.
|
|
25
|
+
- Captured screenshots saved to disk but never read back by the agent.
|
|
26
|
+
|
|
27
|
+
If any item is unmet, the work is mid-iteration. Say so explicitly to the user; offer a precise list of what remains.
|
|
28
|
+
|
|
29
|
+
## Path 1 — Simulator (no device required)
|
|
30
|
+
|
|
31
|
+
The default loop. Always available; deterministic; no device wear; safe for side-effecting flows because nothing real fires.
|
|
32
|
+
|
|
33
|
+
Before trusting a Simulator screenshot, know what it can and can't reproduce: it runs in the Electron preview (a browser-class runtime, not the device — and not the device's JS engine), can't exercise hardware/peripheral features (BLE, printers, raw sockets, SQLite, …), and substitutes fallbacks for Camera and the AI generators (LLM/STT/Vector Store) with Buttress disabled. See [Simulator](simulator.md) for the fidelity boundaries and the per-item gear to run the real implementation — it's what tells you when a green run is enough vs. when to escalate to Path 2.
|
|
34
|
+
|
|
35
|
+
`bun update-app` and `bun deploy-app` publish to the BRICKS portal. The local preview reads `.bricks/build/application-config.json` (produced by `compile`) directly.
|
|
36
|
+
|
|
37
|
+
### Compile tool
|
|
38
|
+
|
|
39
|
+
Typecheck + compile the project. Gate every iteration on this; everything below assumes a clean compile.
|
|
40
|
+
|
|
41
|
+
Agent invocation: call the MCP tool `compile` exposed by the `bricks-ctor` MCP server registered for the project. No arguments.
|
|
42
|
+
|
|
43
|
+
### Simulator tool
|
|
44
|
+
|
|
45
|
+
Use the preview implementation exposed by the current harness:
|
|
46
|
+
|
|
47
|
+
- **CTOR Desktop agent session:** use `simulator_invoke`. CTOR Desktop disables the `bricks-ctor` MCP `simulator` tool and routes screenshots/automation through the desktop preview pane instead. If the user already opened the simulator pane, `simulator_invoke` should reuse it rather than start a separate preview.
|
|
48
|
+
- **Pure `bricks-ctor` project / other agent harness:** use the `bricks-ctor` MCP `simulator` tool when `simulator_invoke` is not available.
|
|
49
|
+
|
|
50
|
+
Both forms are the same verification primitive: launch or reuse Electron preview, take a screenshot, and optionally run a named Automation test by id or partial title. Do not hard-code one tool name into the workflow; choose the available preview tool for the environment.
|
|
51
|
+
|
|
52
|
+
Common arguments:
|
|
53
|
+
- `delay` (ms before screenshot) — default is harness-specific: usually 3000 for a fresh/standalone preview; CTOR Desktop may capture immediately when its preview already has a settled latest config. Increase if Standby Transitions are still in flight.
|
|
54
|
+
- `width`, `height` — screenshot dimensions in px.
|
|
55
|
+
- `responseImage: true` — return the screenshot as image content when the selected model supports vision. If vision is unavailable, save the screenshot and report the path.
|
|
56
|
+
- `testId` or `testTitleLike` — run a project Automation before the screenshot. Use `testTitleLike` for fuzzy matches (case-insensitive partial title).
|
|
57
|
+
|
|
58
|
+
Returns text log + saved screenshot path + image content when supported.
|
|
59
|
+
|
|
60
|
+
### `bun invoke-simulator` (project script)
|
|
61
|
+
|
|
62
|
+
Sustained dev session — watches `subspaces/`, recompiles on save, exposes CDP at `localhost:19852` (configurable via `--cdp-port`), writes `.bricks/devtools.json` with port/pid for downstream tooling.
|
|
63
|
+
|
|
64
|
+
Useful flags:
|
|
65
|
+
- `--screenshot` + `--screenshot-delay/width/height/path` — drive screenshot capture without keeping the window open.
|
|
66
|
+
- `--show-menu` — surface the Electron menu (debugging).
|
|
67
|
+
- `--test-id` / `--test-title-like` — run an Automation in this preview.
|
|
68
|
+
- `--no-keep-open` — exit after one screenshot.
|
|
69
|
+
- `--no-cdp` — disable CDP server (rare; default is to expose it).
|
|
70
|
+
- `--clear-cache` — reset cached state between runs.
|
|
71
|
+
|
|
72
|
+
For ad-hoc CDP inspection against this local preview, connect any CDP client to `localhost:19852` — Chrome DevTools front-end works directly. For an agent-friendly CLI over CDP (screenshot, brick tree/query, input emulation, storage reads, runtime eval, network capture), the `bricks-cli` skill documents the `bricks devtools` command surface — read that skill if it is installed in this workspace. If it is not installed, run `bricks --help` and `bricks devtools --help`; the CLI's own help output is authoritative.
|
|
73
|
+
|
|
74
|
+
To inspect Data / Property Bank or storage state, prefer the dedicated `bricks devtools storage` subcommands — `storage data-bank get <S_xxxx>` (saved Data values), `storage system persist|memory`, `storage system get <key>` — over hand-written `runtime eval`. Reach for `runtime eval` only for *live* store internals that aren't persisted to a data bank (e.g. a current transient value: `runtime eval "system.data.property('S_xxxx', '<alias>')"`); don't reverse-engineer the `system.*` globals.
|
|
75
|
+
|
|
76
|
+
### Project Automations
|
|
77
|
+
|
|
78
|
+
E2E tests authored in TypeScript inside the project (`AutomationTest` / `TestCase`). Test cases include:
|
|
79
|
+
|
|
80
|
+
- `brick_press` — synthesize a press on a Brick.
|
|
81
|
+
- `wait_until_canvas_change` — assert navigation.
|
|
82
|
+
- `assert_property` — assert a Data value equals expected.
|
|
83
|
+
- `wait_property_update` — wait for a Data value to change.
|
|
84
|
+
- `match_screenshot` — visual regression with stored reference image.
|
|
85
|
+
|
|
86
|
+
Trigger types: `launch` (runs on app start), `anytime` (manual), `cron` (scheduled).
|
|
87
|
+
|
|
88
|
+
Important: the automation map id must be `'AUTOMATION_MAP_DEFAULT'` (not a generated id) — the preview test runner reads from `automationMap['AUTOMATION_MAP_DEFAULT']?.map`.
|
|
89
|
+
|
|
90
|
+
Run a single test from the agent: call the available preview tool (`simulator_invoke` in CTOR Desktop, otherwise `bricks-ctor` MCP `preview`) with `testId` or `testTitleLike`.
|
|
91
|
+
|
|
92
|
+
## Path 2 — Real device with DevTools enabled
|
|
93
|
+
|
|
94
|
+
Required when the deployment depends on hardware behaviour the Electron preview cannot reproduce: physical orientation/DPI, real touch hardware (IR overlays, multi-touch), peripherals (camera, BLE, MQTT, NFC, payment, printer, sensors), watchdog cycles on the actual launcher build, fleet-managed reboot semantics, LocalSync across multiple devices, the actual color/luminance of the panel.
|
|
95
|
+
|
|
96
|
+
**Always Path 2** when the brief touches: payment, identity capture, real-time peripheral data, multi-device LocalSync, certified hardware.
|
|
97
|
+
|
|
98
|
+
### One-time manual setup (the agent cannot do this)
|
|
99
|
+
|
|
100
|
+
Ask the user to:
|
|
101
|
+
|
|
102
|
+
1. On the device, open **Settings** → advanced settings.
|
|
103
|
+
2. Toggle **Chrome DevTools** on. The device starts a DevTools server on the local network on port `19851` (auto-increments if taken).
|
|
104
|
+
3. Confirm **Enable LAN Discovery** is on (default) so the device is discoverable.
|
|
105
|
+
4. Note the device passcode if displayed.
|
|
106
|
+
|
|
107
|
+
Requires BRICKS Foundation **≥ 2.24** for CDP commands.
|
|
108
|
+
|
|
109
|
+
### Endpoints exposed once enabled
|
|
110
|
+
|
|
111
|
+
| Endpoint | URL shape | Use |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| Web UI | `http://<ip>:19851` | DevTools landing in a browser |
|
|
114
|
+
| CDP | `http://<ip>:19851/devtools-frontend/inspector.html?ws=<ip>:19851/ws/<passcode>` | Chrome DevTools front-end |
|
|
115
|
+
| MCP | `http://<ip>:19851/mcp` | MCP endpoint (Bearer-token auth with passcode) |
|
|
116
|
+
| MCP SSE | `http://<ip>:19851/sse` | Same, SSE transport |
|
|
117
|
+
| Info | `http://<ip>:19851/devtools/info` | Device / server metadata |
|
|
118
|
+
|
|
119
|
+
### Driving the device
|
|
120
|
+
|
|
121
|
+
Once DevTools is on, ask the user how they want to drive the verification — Chrome DevTools front-end, an MCP client they already run, or screenshot export from their own tooling — and follow their lead. Capture every Canvas screenshot through whichever path the user picks; the verification gate is about the evidence, not the tool.
|
|
122
|
+
|
|
123
|
+
For agent-driven CDP/MCP work against the device (`bricks devtools …` with `-a <ip> --passcode <pc>`, plus bridging the device MCP endpoint into an MCP client), the same `bricks-cli` skill referenced in Path 1 covers the on-device case — read it if installed. If not installed, run `bricks --help` and `bricks devtools --help` for the authoritative command listing.
|
|
124
|
+
|
|
125
|
+
### Running real-device Automations from an agent
|
|
126
|
+
|
|
127
|
+
There is no `bricks devtools automation` subcommand. Use the DevTools runtime helpers exposed inside the app:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
bricks devtools runtime eval -a <ip> -p 19851 --passcode <pc> "Object.getOwnPropertyNames(automation).sort()" -j
|
|
131
|
+
bricks devtools runtime eval -a <ip> -p 19851 --passcode <pc> "automation.list()" -j
|
|
132
|
+
bricks devtools runtime eval -a <ip> -p 19851 --passcode <pc> "automation.run('<TEST_id>', { updateScreenshot: false })" --await -j
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`automation.run()` starts the device-side run and may return `null`; treat that as accepted, not as a pass/fail result. Wait for the run timeout/window, then verify completion through the app's own state and a screenshot. For app state, use live runtime reads such as:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
bricks devtools runtime eval -a <ip> -p 19851 --passcode <pc> "JSON.stringify({ result: system.data.property('<S_xxxx>', '<resultAlias>')?.value })" -j
|
|
139
|
+
bricks devtools screenshot -a <ip> -p 19851 --passcode <pc> -o /tmp/device-automation.png
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
In CTOR Desktop sandboxed sessions, keep these as separate simple commands. Avoid multi-line shell scripts, `for` loops, brace expansion, and command substitution around `bricks devtools`; those can stay sandboxed and lose LAN/device access. After `bun update-app` or a device refresh, the DevTools socket may briefly drop, so wait and probe with one screenshot or one `runtime eval` before running the automation.
|
|
143
|
+
|
|
144
|
+
### Real-device side-effects warning
|
|
145
|
+
|
|
146
|
+
Real devices fire real peripherals. Payment terminals charge. MQTT broadcasts on shared topics. BLE advertises to bystanders. Use a **staging** device for verification cycles; never iterate on a production-deployed device unless the user explicitly approves.
|
|
147
|
+
|
|
148
|
+
## Path 3 — Remote debugging via BRICKS Controller (off-LAN)
|
|
149
|
+
|
|
150
|
+
Out of scope for the standard verification loop. It exists for ops scenarios where the device is not on the same network. If the user asks for it, point them at the BRICKS Controller documentation rather than attempting it as part of verification.
|
|
151
|
+
|
|
152
|
+
## Decision rule
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
default Path 1.
|
|
156
|
+
|
|
157
|
+
if deployment depends on:
|
|
158
|
+
physical orientation / DPI / touch HW / peripherals /
|
|
159
|
+
watchdog on real launcher / LocalSync / certified hardware
|
|
160
|
+
→ escalate to Path 2 before declaring done.
|
|
161
|
+
|
|
162
|
+
if brief touches payment, identity, peripherals, LocalSync
|
|
163
|
+
→ Path 2 is mandatory, not optional.
|
|
164
|
+
|
|
165
|
+
if user is off-LAN
|
|
166
|
+
→ Path 3 (out of scope here).
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## What to actually verify (per shape)
|
|
170
|
+
|
|
171
|
+
### Static signage (single-canvas glance loop)
|
|
172
|
+
- Path 1 screenshot captures the loop frame.
|
|
173
|
+
- Watch one full rotation in `bun invoke-simulator` to see all queue items.
|
|
174
|
+
- Cut network mid-loop; confirm cached media plays.
|
|
175
|
+
- Path 2 only if the panel is OLED / has burn-in concerns or specific color profile.
|
|
176
|
+
|
|
177
|
+
### Multi-canvas state machine (kiosk)
|
|
178
|
+
- Path 1: Automation walking the happy path with `brick_press` + `wait_until_canvas_change` + `assert_property` + `match_screenshot` per Canvas.
|
|
179
|
+
- Cancel + inactivity reset paths verified as Automations.
|
|
180
|
+
- Watchdog reset: kill the runtime, restart, confirm boot Canvas resets transient flow Data.
|
|
181
|
+
- Path 2 mandatory if payment / identity is in the flow — fire a real test transaction on staging hardware.
|
|
182
|
+
|
|
183
|
+
### Subspace-driven composition
|
|
184
|
+
- Path 1: open the Subspace standalone if the preview supports it; verify in isolation.
|
|
185
|
+
- Embedded test: walk the host's flow; assert Outlets fire as expected via `wait_property_update`.
|
|
186
|
+
- Re-mount test: cause the host to re-bind the Subspace; confirm internal state resets.
|
|
187
|
+
|
|
188
|
+
### Generator-driven reactive
|
|
189
|
+
- Path 1: drive the source by writing to Data from the runtime (whatever CDP path the user prefers); observe Brick re-render.
|
|
190
|
+
- Throttle / firehose tests: write 100 updates rapidly; confirm the canvas doesn't choke (frame budget intact).
|
|
191
|
+
- Disconnect: simulate source disconnect; confirm UI surfaces stale state, not blank.
|
|
192
|
+
- Threshold transitions: cross every Switch boundary; confirm visual reaction.
|
|
193
|
+
- Path 2 mandatory if the source is a real peripheral — Electron cannot fake the timing or the failure modes.
|
|
194
|
+
|
|
195
|
+
### Multi-device LocalSync
|
|
196
|
+
- Path 2 mandatory. Two devices on the same LAN with DevTools on. Drive one, observe the other.
|
|
197
|
+
|
|
198
|
+
## Browser / runtime log discipline
|
|
199
|
+
|
|
200
|
+
Throughout: the DevTools console must be clean. 404s on media, "Data key not defined" warnings, Generator init failures, React-style warnings — every one is a defect or accept-with-comment. Don't ship around them.
|