igneum/docs/plans/miner-ui-5.md
igneum-labs 6908631196 miner-ui-5: live-dag.js 2.0.2 (master 6c70b948, byte-identical), fps 60 and narrow on the phone-width card, four captures reshot
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 0cb2d9a676)
2026-10-07 10:43:51 +00:00

98 lines
14 KiB
Markdown

# Igneum Miner UI 5: the ladder shown everywhere, the miner's first month
7 October 2026, 10:1x to 11:xx UK. Mission item 6 (docs/analysis/mission/mission.md 2.6; the brief is
docs/analysis/mission/reinvent.md 1, 2, 3.2 to 3.7 and 7). Branch `miner-ui-5` from the miner-ui-4 tip 5d9c55a2
(release-0.3.18, since renumbered 0.3.19), worktree `../igneum-wt-miner-ui-5`. Nothing published; the shipper cuts it.
The site lane (a4b202cabca2d95c0) owns `/live` and `/miners`; the node lane (a283f5f0d364ceef0) owns the RPC fields
listed in section 6.
## 1. What is built
| Part | Where | What |
|---|---|---|
| (a) The Poisson count-up | Overview, `#first-wait`; `View.poisson` | from the first job until the first block of this install: "A card like yours finds a block about every 1.6 hours on today's network. Chance so far: 31%." The gap is `/api/stats hashrate` over the fleet rate (`mining.hash_total`) at one block a second; the chance is `1 - e^(-elapsed / gap)` from the moment mining started this run. Over an 8-hour gap the pool line sits under it. The ring fills with the chance. Hover names the two fields and the formula. |
| (b) The block card | Overview, `#block-card`; `View.blockCard`; `src/ladder.rs` | raised by the engine on the first block of the install, every 100th, 1,000th and 10,000th, and the first block of a new card (`ladder.milestone`); in place, never a dialog; stays until dismissed (the key is stored, so a relaunch does not re-raise it). "Block 1,284,117 is yours." (the DAA score the node logged), the card and its rate and share, "4.88 IGN to 0xdd44…86E8 (72 producer points, 8 for signing, ramp 19%)", "rank 41 of 1,204 keys · day 23 of 30", the hash, Open in the explorer (`/block/<hash>`), Save the card, Copy the link. The hash joins the miner's `ACCEPTED block nonce=` line to the node's `PoW accepted <hash> … nonce` line by nonce (the node logs every block it validates). |
| Save the card | `drawCard` in app.js, `POST /api/card`, `src/card.rs`, `platform::reveal_file` | a 1200x630 PNG drawn on a canvas from the card's own words with the fonts the app ships (no network call): the mark, "Block N is yours", the card and rate, the IGN line, rank and day, the miner key and hash, the explorer URL. The miner's object, never the project's number: no network rate, no block count. The engine writes it under `<data root>/cards/` and reveals it in Finder or Explorer. `u00-block-card-1200x630.png` is the one the mock drew. |
| (c) Earnings in IGN first | Earnings, the money card; `View.earningsLines` | line 1 "7,680 IGN a day at your last hour's rate (4 blocks, 80.00 IGN each) · about 6,912 IGN expected at 100 MH/s" (measured first, the expectation beside it; the expectation alone before the first block); line 2 "2,592 blocks in 30 days, 207,360 IGN" from `getFinalityWeights keys[].blocks` at today's reward, with the shards; line 3 the balance (unchanged); line 4 the typed price: a £ per IGN the user types, kept in the page's storage, never fetched, "about £384 a day at £0.05 per IGN, your figure · no exchange lists IGN"; line 5 "your cards are 0.100% of the network · weight rank 41 of 1,204 keys · 23 of 30 days"; line 6 electricity unchanged plus "2,133 IGN per kWh"; the dev fee unchanged. Every number's `title` names its field. |
| The per-card line | Cards rows | an "IGN a day" cell per card from its rate, the network rate and the reward (hover: the fields) |
| (d) The ladder | Earnings `#ladder-card` (the rungs with their lines and hovers), Overview `#ladder-strip` (the seven chips and the current rung's line); `View.ladderRungs` | rungs: first block; your key has a vote; your signature is in checkpoint K; full window; rank by weight; signing streak; your card proved a shard. Every threshold is read from the node's `params` (`dust`, `weightWindow`, `presenceWindow`), never a constant: the devnet reads "5 blocks to a vote" and "2 of 2 hours", mainnet "100 blocks" and "30 of 30 days". The words, in copy law: "Your key has a vote." "Your signature is in checkpoint 184,220." "30 of 30 days. Full weight." "Rank 41 of 1,204 keys." "Signing 8 of 80 points, 41 days unbroken." "Your card proved shard 3 of block 1,284,117." |
| The chain facts | `GET /api/ladder`, `src/chainfacts.rs` | the node's `igneum_getFinalityWeights` on the EVM port first (OWED on the node, section 6), else the observer's `/api/live finality.weights` (the same reply relayed, keys cut to 64, ids as 8 hex) plus `/api/stats`; this machine's keys ranked by `blocks`; cached 10 s; the reply names its source and the dashboard says it ("through the observer" until the node answers). |
| The machine's record | `src/ladder.rs`, `<app dir>/ladder.json`, `api/state.ladder` | first block (time, card, hash, DAA), blocks per UTC day (the days-mined ring), the checkpoints signed and which locked (`VOTE index=` and `LOCK checkpoint` lines), the signing streak (a gap over the presence window ends it), the paid shards (`src/prover.rs`), the milestone, the first-hour marks (node synced, card mining). |
| (e) The public profile opt-in | Settings "Make my page public"; `settings.profile_public`, `api/settings {profile_public}` | off by default; the words say what becomes public (key ids, blocks, rank, presence, card model). The site reads the switch through the app's existing report path (owed to the site lane; today the switch is stored and shown, nothing leaves the machine). |
| (f) The first-hour timeline | Earnings "Your first hour"; `View.timeline` | Installed (`settings.installed_at`), App started, Node synced (`ladder.first_synced_at`), Card mining (`ladder.first_mining_at`), First block, First payout (the coinbase of the first block), each as "+N min" from the install. |
| Prove | `#shard-card` | "Your card proved shard 3 of block 1,284,117." with the IGN, on the newest paid record |
| Activity | `EVENT_MAP` | "Your first block, found by RTX 4070", "Block 1,000 of this machine, found by …", "First block on …" |
## 2. The chain scene (EMBER 02, taken from the site lane)
the project lead's renderer pack (11:3x UK): `ui/live-dag.js` (sha256 82d56565…, IgneumDag 2.0.2, master 9162c847: the phone-width options narrow/narrowWindow 30/narrowLanes 4/narrowMinNode 11, hairlines off when narrow, percent-only checkpoint labels; the app passes fps 60 on both mounts and narrow when the window is under 720 px on the compact card, since compact never auto-narrows; 2.0.1 e78f2d38… added the site lane's real-data options, maxLanes 7 with the own key first and an OTHERS lane, minNode 9.5, merge hairlines within two lanes, provenGlow; 2.0.0 was 582f2e73…) and `ui/proof-core.js` (c2a094a6…,
IgneumProof 2.0.0) are byte-identical copies of the site-ui-4 files; the engine serves `proof-core.js` beside
`live-dag.js`. The Overview card mounts the compact mode (`compact: true, window: 60, poll: false`); Inspect, or a
click on a block, opens the full scene under it with the block inspector in the site's words (Proof in progress /
Proof complete / Checkpoint locked / Block observed; Proof shards, Inclusion, Miner key, Blue score, DAA score,
Parent links, Header time, Checkpoint, Finality; the note on separate states), the filter tabs (All blocks, Selected
chain, Your blocks), the window stepper, Follow and Pause. One owner of the feed: the page reads the engine's
`api/live?window=120` every 2 s and pushes the reply into both scenes; `mine` matches every vote key id of this
machine's cards (the 8 hex the feed names as `b.miner`), never the payout address. `--included` and `--excluded` are on
`:root` for both themes. The fallback strip (this machine's own blocks) draws after three failed reads. The legend
reads "Your blocks" as in the reference.
Caption fact from the site lane: the observer's per-block shard states read "planned" for every chain block in the window while its paid counters move (10:08 UK), so the live scene shows no proven block until the observer's read is fixed (the fleet lane has it); the mock's feed shows the proven state.
## 3. Gate results
| Gate | Result |
|---|---|
| A Devnet 2 key walks every rung on screen in a view test | `view.test.mjs` "a Devnet 2 key walks every rung on screen": key 6ad0e117 under the finality params the devnet node reports (`api/live params`, 7 Oct 2026: dust 5, weightWindow 7,200, presenceWindow 20; Devnet 2's override file sets none of them, so it runs the same values) goes nothing → first block → vote → signature in checkpoint 8,496 → 2 of 2 hours → rank 3 of 4 → 15 min unbroken → shard 3 of block 1,284,117; every rung's `field` matches `getFinalityWeights|getFinalityCheckpoints|igneum_getProofRecords|/api/stats|this app`. The mainnet-sized walk (100 blocks, 30 days, the observer's cap of 64) is the next test. |
| Every number names its RPC field on hover | `View.FIELDS` (23 sentences) in every `title`; the rungs, the Earnings lines, the block card lines, the Cards cell, the count-up |
| The PNG opens; no network call is made to make it | `u00-block-card-1200x630.png` drawn by the mock page through Playwright's chromium; the page's only calls while drawing are `document.fonts.load` of the app's own woff2 files |
| "First share within 10 minutes of download in 9 of 10 fresh Windows installs" | written as a runbook for the shipper, not run: `docs/plans/miner-ui-5-first-share-runbook.md` |
| Tests | UI 47 (42 before; the five new ones); the app crate on the box 190 + 27 + 8 (ladder 10, chainfacts 4, card 3 among them) |
## 4. Captures (`docs/plans/miner-ui-5-shots/`, the ui-mock's scenarios firstwait, firstblock, ladder)
`u00-block-card-1200x630.png` the saved card; `u01-overview-firstwait-dark-1440` the count-up and the strip; `u02`/`u03`
the first block's card, dark and light; `u04`/`u14` the full scene with a block in the inspector, dark and light;
`u05`/`u06` Earnings with the ladder and the timeline open; `u07` the shard card; `u08` the Cards row with IGN a day;
`u09` Settings with the profile switch; `u10` to `u13` the Overview and Earnings at 390, both themes. Captured through
Playwright's own chromium (never Google Chrome.app) against `tools/ui-mock/server.mjs 4319`.
## 5. Owed by this lane, and what the site lane and the Discord lane take
| What | Who |
|---|---|
| The Cards setup screen's "about N blocks a day" from the priors file (reinvent 3.2, Cards rows) | this lane, once the priors ship in the signed manifest (the file lives on the site today) |
| The QR of the explorer link on the saved card (reinvent 3.3) | this lane; the URL is printed in full instead |
| The opt-in reaching the site (the address page's noindex lifted for a key whose app switched it on) | the site lane and this lane together: the switch rides the app's existing report upload as `profile_public` |
| `/live` weight leaderboard and `/miners` census with the same words | the site lane; the field list and the rung words were sent to it (section 7) |
| `#first-blocks` posts and the Voter and Window roles | the Discord lane; the shapes are in `docs/community/discord/ladder.md`, nothing posted |
| Sound on a block | not built (verdict table: watch) |
## 6. RPC fields the node does not expose yet (sent to the node lane)
| Field | Why the app needs it | What the app does meanwhile |
|---|---|---|
| `igneum_getFinalityWeights` on the EVM JSON-RPC port (the wRPC `getFinalityWeights` reply as is) | the engine carries no wRPC client; today the facts come through the observer's relay, which cuts the table to 64 keys and so cannot rank a key below 64 | reads `/api/live finality.weights` and says "through the observer" on hover; "Below the top 64 keys" when the key is not listed |
| `igneum_getFinalityCheckpoints` the same way | the locked index and the certificate list from this machine's node | `/api/stats finality.latest_locked_index` |
| `getFinalityCheckpoints checkpoints[].signers` (the key hashes in the certificate bitmap), or a per-key `lastSignedLockedIndex` | "Your signature is in checkpoint K" as a chain fact | the app's own `VOTE index=` lines, read as locked once a `LOCK checkpoint` line reaches them; the hover says so |
| `getFinalityWeights keys[].daysMined` (distinct days with a blue block in the window) and `keys[].firstBlockDaa` | "30 of 30 days" as a chain fact; the Window role; the `/live` days-mined column | the app's own day ring (`ladder.json`), which only counts what this install saw |
| `getFinalityWeights keys[].streakDays` or `keys[].lastMissedDaa` | the signing streak as a chain fact | the app's own unbroken-vote reading; `participation` is shown beside it |
| `getFinalityWeights keys[].rank` or a `getFinalityKey {keyHash}` reply with blocks, voter, participation, rank, daysMined, streak | one call per machine instead of the whole table | the table sorted on the client |
| `igneum_getProofRecords` by key (the paid shards of a key over a range) | "your card proved shard N of block M" for records this install did not watch | `src/prover.rs` reports what it submitted and saw paid |
## 7. Sent to the site lane (so `/live` and `/miners` match the app word for word)
The rung names and lines of section 1 (d); the fields: `getFinalityWeights params.dust / weightWindow / presenceWindow`,
`keys[].blocks / voter / participation / strippedUntilDaa / pubkey`, `totalWeight`, `activeWeight`, `voters`,
`checkpointIndex`; `getFinalityCheckpoints latestLockedIndex / nextIndex / finalityActive`; `/api/stats hashrate`,
`block_reward.miner_ign`, `block_reward.ramp_factor`; the leaderboard column order (rank, key id, blocks in window,
days mined, presence, last block) with "weight, never hashrate: a card that arrived today sits at the bottom".
## 8. Copy added (the copy law: no em dashes, no two-beat antithesis, no aphorisms; every line names a number or an action)
"Waiting for your first block." "A card like yours finds a block about every 1.6 hours on today's network. Chance so
far: 31%." "Above an 8-hour gap a pool pays by the share, every minute. The pool setting arrives with pool-0." "Block
1,284,117 is yours." "Open in the explorer" "Save the card" "Copy the link" "Your key has a vote." "61 of 100 blocks to
a vote." "Your signature is in checkpoint 184,220." "30 of 30 days. Full weight." "Rank 41 of 1,204 keys." "Below the
top 64 keys." "Signing 8 of 80 points, 41 days unbroken." "Your card proved shard 3 of block 1,284,117." "Proving is
off." "Type a price" "Use it" "Make my page public" "Your first hour" "The ladder".