The blank /live (the project lead, 14:3x UK): IgneumDag 2.0.2 painted only from a requestAnimationFrame loop gated on document.hidden and the IntersectionObserver, so a page that loaded with document.hidden true (the desktop pane, a background tab) and whose embedder never fired visibilitychange took every push, reported live and never drew a frame. 2.0.3 paints the current picture synchronously on push, size and theme change; the motion loop alone waits for a visible document and an intersecting canvas. Confirmed headless on build-2 against the live feed: hidden document 0 lit pixels before, 110,007 after; the never-intersecting observer repaints on every push. Known-failed test tools/scene/paint-test.cjs (the 2.0.2 renderer draws nothing in the same world). The second 2.0.3 change: the phone rule (30 s window, four lanes) keys on the viewport width, not the canvas width; a 640 px hero on a 1,440 px laptop was rendering as a phone while the app's card beside it was not. scene/ is the one source: live-dag.js, proof-core.js, tokens.css (the fourteen palette tokens, the brand package's values, dark and light), feed-contract.md and .json (one JSON shape for the observer's /api/live and the app's api/live), a recorded reply as the fixture. tools/scene/sync.mjs writes the copies and the scene-tokens block into site.css and app.css; --check is the gate line (byte-equal scripts, an equal block, the names defined nowhere else, a print block excepted), --self-test fails five known cases first. The site's token definitions move out of the package's :root line into the block; no value changes on the site. tools/scene/feed-contract.mjs validates a reply against the key lists; its test refuses a miner rewritten to "you", a float now, a stray key. Three new lines in tools/ci/pre-push.sh. The app side (branch scene-parity, for 0.3.21) takes the same folder. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
35 lines
2.9 KiB
Markdown
35 lines
2.9 KiB
Markdown
# scene: the chain scene, once
|
|
|
|
The live devnet animation on the site's home fold and `/live`, and the miner app's "The chain, live" card and its Inspect view,
|
|
are ONE renderer fed ONE JSON shape. This folder is the source; everything else is a copy or a reader.
|
|
|
|
| File | What it is | Copies |
|
|
|---|---|---|
|
|
| `live-dag.js` | EMBER 02 `IgneumDag` 2.0.3: lanes per vote key, parent curves, the selected chain, checkpoint bands, proof glow | `site/live-dag.js`, `app/igneum-app/ui/live-dag.js` |
|
|
| `proof-core.js` | `IgneumProof` 2.0.0: the selected block's shards | `site/proof-core.js`, `app/igneum-app/ui/proof-core.js` |
|
|
| `tokens.css` | The fourteen palette tokens the two scripts read from `:root`, dark and light, the brand package's values | the `scene-tokens` block in `site/site.css` and `app/igneum-app/ui/app.css` |
|
|
| `feed-contract.md`, `feed-contract.json` | The feed shape, in words and as key lists | read by `tools/scene/feed-contract.mjs` and the app's `src/live.rs` test |
|
|
| `fixtures/live-2026-10-07.json` | One recorded `/api/live?window=300` reply | the parity, paint and contract tests |
|
|
|
|
`node tools/scene/sync.mjs` writes the copies; `--check` is the gate line (byte-equal scripts, an equal token block, the token
|
|
names defined nowhere else, a print block excepted); `--self-test` proves the check on known-failed cases first. A side (site or
|
|
app) takes part once its `live-dag.js` copy exists; a `release-*` branch checks the app's copies only, because the release
|
|
branches carry the site tree as it was when they were cut and the site deploys from master alone.
|
|
|
|
## The one rule for the two surfaces
|
|
|
|
Same renderer, same tokens, same feed, same mount options: `window: 60`, `fps: 60`, `poll: false`, the page polls
|
|
`/api/live?window=300` every 2 s and pushes. What may differ is the box the scene sits in (the home hero is 500 px tall, `/live`
|
|
420 px, the app's card 220 px in `compact` mode and its Inspect view the `/live` height), and the app's viewpoint: its `mine`
|
|
option names this machine's vote keys, so its own blocks glow and its lane reads YOUR KEY. That is an overlay on the same
|
|
picture, never a second renderer, never a rewritten feed. The phone rule (30 s window, four lanes, bigger nodes) keys on the
|
|
viewport width under 720 px, not on the canvas width: a 640 px hero on a laptop is not a phone.
|
|
|
|
## The tests (all in `tools/ci/pre-push.sh`)
|
|
|
|
| Line | What it proves |
|
|
|---|---|
|
|
| `tools/scene/sync.mjs --self-test && --check` | the copies are the source, the tokens live once |
|
|
| `tools/scene/paint-test.cjs` | a push paints with the document hidden, the observer silent and no animation frame (the blank `/live` of 7 October 2026) |
|
|
| `node --test tools/scene/feed-contract.test.mjs` | the fixture validates; a rewritten miner, a float `now`, a stray key are refused |
|
|
| `tools/scene/parity-remote.sh` | on build-2: the fixture through the site's `/live` and the app's UI, three frames each, pixel-equal apart from the app's lane label; a changed token fails first |
|