ship-a-browser-surfacelisted
Install: claude install-skill Ybx-jp/thalamus
# Ship a Browser Surface
Three surfaces, three build disciplines, one verification ladder. The failure this
skill exists to prevent is the one where the change is right and every check passes
and the operator's phone still shows something else.
## The three surfaces are not alike
| surface | source | build | tests |
|---|---|---|---|
| console PWA | `src/thalamus/console/static/` | **none** — files served as written | `tests/js/*.test.mjs` under node, driven by `tests/test_console_js.py` |
| graph viewer | `frontend/` (React 19, Vite, TS) | `npm run build` → `src/thalamus/viewer/static/` | `npm test` (vitest) |
| pulse | `src/thalamus/pulse/static/index.html` | none — one file | none |
The console is dependency-free on purpose: one of the server's jobs is restarting
the systemd unit that hosts it, so the fewer moving parts between a tap and a tmux
call the better. Do not add a bundler, a framework, or a package to it. The viewer
is the opposite and already carries its toolchain — use it there.
**The viewer's build output is committed.** A source change in `frontend/` that is
not rebuilt ships nothing; the served bundle is whatever was last written into
`src/thalamus/viewer/static/assets/`.
## The ladder — run in this order
The first three are seconds and catch most of it.
1. **`cd tests/js && node <each>.test.mjs`** — the whole JS suite, about a second,
no Python. This is the inner loop.
2. **`uv run pytest tests/test_console_js.py -q`** — adds the two Python-side