igneum/scene/README.md
igneum-labs a9f5b5fd4d Chain scene 2.0.3: /live and the home fold paint on every push whatever the document's visibility says; the renderer, its palette and its feed contract move to one shared folder scene/ with byte-equal copies checked by the gate (7 October 2026, 15:2x UK)
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>
2026-10-07 13:29:38 +00:00

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 |