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> |
||
|---|---|---|
| .. | ||
| fixtures | ||
| feed-contract.json | ||
| feed-contract.md | ||
| live-dag.js | ||
| proof-core.js | ||
| README.md | ||
| tokens.css | ||
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 |