miner-ui-4: the chain's own words come back (shard, segment, checkpoint, aggregator, verifying), each explained once in grey; the rest of the sweep stands

Main's correction: the app uses the same words as the site, the litepaper and the ledger for the chain's own objects,
or users cannot match the app to the docs. Shard (not piece), segment (not run of blocks, 'runs of 8 blocks' as the
explanation), checkpoint and 'locked checkpoint' (not lock point), aggregator (not combiner), verifying (not checking
proofs). Card, app, Default · Balanced, the Ember Tune strip, the state words and the one-sentence rule stay. 37 UI
tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-06 18:59:59 +00:00
parent 7ce5965e46
commit 4af9ab4d0a
4 changed files with 36 additions and 33 deletions

View file

@ -499,17 +499,17 @@ var View = (function () {
// the Prove page: pv = state.proving, enabled = state.settings.prove, synced = state.node.synced
function proveWords(pv, enabled, synced) {
pv = pv || {};
if (!enabled) return { word: 'off', sub: 'not proving', tone: 'off', note: 'Off. Switch it on and this machine proves the pieces it is given.' };
if (!enabled) return { word: 'off', sub: 'not proving', tone: 'off', note: 'Off. Switch it on and this machine proves the shards it is given.' };
if (pv.status === 'setup' || (pv.enabled && !pv.available && pv.setup_hint)) return { word: 'needs setup', sub: 'one-time install', tone: 'bad', note: pv.setup_hint || pv.message || 'The prover is not installed yet.' };
if (!synced || pv.status === 'waiting') return { word: 'waiting', sub: 'for the node to sync', tone: '', note: pv.message || 'Proving starts once the node has every block.' };
if (pv.status === 'proving') return { word: 'proving', sub: pv.current || 'a piece', tone: 'on', note: pv.message || 'A proof takes minutes on a CPU and seconds on an NVIDIA card.' };
if (pv.status === 'proving') return { word: 'proving', sub: pv.current || 'a shard', tone: 'on', note: pv.message || 'A proof takes minutes on a CPU and seconds on an NVIDIA card.' };
if (pv.status === 'submitted') return { word: 'submitted', sub: 'waiting to be paid', tone: 'on', note: pv.message || 'The node checks the proof, then the chain pays it.' };
if (pv.status === 'idle') return { word: 'idle', sub: 'nothing assigned', tone: 'ok', note: pv.message || 'On. Nothing to prove right now. The next piece arrives within a minute.' };
if (pv.status === 'idle') return { word: 'idle', sub: 'nothing assigned', tone: 'ok', note: pv.message || 'On. Nothing to prove right now. The next shard arrives within a minute.' };
return { word: pv.status || 'on', sub: pv.backend ? pv.backend.toUpperCase() : '', tone: '', note: pv.message || '' };
}
function verifierWords(pv) {
var vm = (pv && pv.verifier_mode) || 'unknown';
var word = { command: 'checking proofs', trust: 'trusting without checking (devnet only)', off: 'off, passing proofs along', unknown: 'not read yet' }[vm] || vm;
var word = { command: 'verifying', trust: 'trusting without verifying (devnet only)', off: 'off, passing proofs along', unknown: 'not read yet' }[vm] || vm;
if (vm === 'command' && pv.pool_entries) word += ' · pool ' + pv.pool_verified + '/' + pv.pool_entries + (pv.pool_failed ? ', ' + pv.pool_failed + ' rejected' : '');
return { word: word, tone: vm === 'command' ? 'ok' : (vm === 'off' || vm === 'trust') ? 'warn' : 'dim', needsSetup: vm === 'off' && /WSL2 prover is not installed/.test((pv && pv.verifier_reason) || '') };
}
@ -518,7 +518,7 @@ var View = (function () {
function proveTier(cd) {
var gb = cd.vram_mb ? Math.round(cd.vram_mb / 1024) : 0;
if (cd.vendor === 'apple') return cd.name + ': proves on the CPU, slowly.';
if (cd.vendor === 'nvidia') return gb >= PROVE_GB ? cd.name + ': proves while it mines (' + gb + ' GB; a full piece needs ' + SHARD_GB + ' GB).' : cd.name + ': cannot prove a full piece (' + gb + ' GB; a full piece needs ' + SHARD_GB + ' GB).';
if (cd.vendor === 'nvidia') return gb >= PROVE_GB ? cd.name + ': proves while it mines (' + gb + ' GB; a full shard needs ' + SHARD_GB + ' GB).' : cd.name + ': cannot prove a full shard (' + gb + ' GB; a full shard needs ' + SHARD_GB + ' GB).';
return cd.name + ': cannot prove yet (proving needs an NVIDIA card).';
}
// the counts in one sentence: "idle · nothing assigned · 0 assigned · 0 proven · 0 paid · 0.0000 IGN"
@ -619,11 +619,11 @@ var View = (function () {
[/^GPU worker built from source/, function () { return 'Mining program built'; }],
[/^block (\d+) shard (\d+) paid (.+) IGN/, function (m) { return 'Proof paid: ' + m[3] + ' IGN'; }],
[/^block (\d+) shard (\d+) proven and submitted in (\d+) s/, function (m) { return 'Proof sent in ' + m[3] + ' s'; }],
[/^segment .+ paid (.+) IGN to the aggregator/, function (m) { return 'Run of blocks paid: ' + m[1] + ' IGN'; }],
[/^segment (\S+)\.\.(\S+) aggregated and submitted in (\d+) s/, function (m) { return 'Run of blocks proven in ' + m[3] + ' s'; }],
[/^segment (\S+)\.\.(\S+): (\d+) shards proven, aggregated and submitted in (\d+) s/, function (m) { return 'Run of blocks proven: ' + m[3] + ' pieces in ' + m[4] + ' s'; }],
[/^segment (\S+)\.\.(\S+): (\d+) shards proven and submitted in (\d+) s; the segment record is held/, function (m) { return 'Run of blocks proven: ' + m[3] + ' pieces in ' + m[4] + ' s. Its record waits for the chain'; }],
[/^segment (\S+)\.\.(\S+) record accepted on retry/, function () { return 'Run of blocks accepted'; }],
[/^segment .+ paid (.+) IGN to the aggregator/, function (m) { return 'Segment paid: ' + m[1] + ' IGN'; }],
[/^segment (\S+)\.\.(\S+) aggregated and submitted in (\d+) s/, function (m) { return 'Segment proven in ' + m[3] + ' s'; }],
[/^segment (\S+)\.\.(\S+): (\d+) shards proven, aggregated and submitted in (\d+) s/, function (m) { return 'Segment proven: ' + m[3] + ' shards in ' + m[4] + ' s'; }],
[/^segment (\S+)\.\.(\S+): (\d+) shards proven and submitted in (\d+) s; the segment record is held/, function (m) { return 'Segment proven: ' + m[3] + ' shards in ' + m[4] + ' s. Its record waits for the chain'; }],
[/^segment (\S+)\.\.(\S+) record accepted on retry/, function () { return 'Segment record accepted'; }],
[/^WSL2 prover setup started/, function () { return 'Prover setup started in its own window'; }],
[/^Card removed: (.+); its worker stopped/, function (m) { return 'Card removed: ' + m[1]; }],
[/^(.+): not usable \((.+?)\); its worker stopped/, function (m) { return m[1] + ': not usable (' + m[2] + ')'; }],
@ -995,7 +995,7 @@ if (typeof document !== 'undefined') (function () {
$('r-wallet').addEventListener('click', function () { api('api/open', { url: 'https://igneum.network/wallet' }); });
// ---------- Prove ----------
$('s-prove').addEventListener('change', function () { var on = this.checked; api('api/prove', { on: on }).then(function (r) { if (r.ok) toast(on ? 'Proving on. The first piece arrives within a minute' : 'Proving off'); else { toast(r.error || 'could not change'); $('s-prove').checked = !on; } }); });
$('s-prove').addEventListener('change', function () { var on = this.checked; api('api/prove', { on: on }).then(function (r) { if (r.ok) toast(on ? 'Proving on. The first shard arrives within a minute' : 'Proving off'); else { toast(r.error || 'could not change'); $('s-prove').checked = !on; } }); });
$('pv-setup').addEventListener('click', function () { api('api/prove/setup', {}).then(function (r) { toast(r.ok ? 'Setup started in its own window' : (r.error || 'could not start')); }); });
// ---------- Settings ----------
@ -1570,7 +1570,7 @@ if (typeof document !== 'undefined') (function () {
$('n-switches').innerHTML = (n.consensus_switches || []).map(function (x) { return '<span class="' + (sw && x.key === sw.key ? 'next' : x.daa <= n.daa ? 'past' : '') + '"><span>' + esc(x.name) + (x.daa <= n.daa ? ' (applied)' : '') + '</span><span>' + withCommas(x.daa) + '</span></span>'; }).join('');
}
if (f.last_lock > 0) { setText('f-lock', '#' + withCommas(f.last_lock) + ' · ' + rel(f.age_s)); setText('f-note', 'a point the miners agreed can never be undone. It locks at two thirds of the last 30 days of mining'); }
else { setText('f-lock', 'none yet'); setText('f-note', f.message || 'locks appear once this machine votes'); }
else { setText('f-lock', 'none yet'); setText('f-note', f.message || 'checkpoints lock once this machine and the others vote'); }
setText('f-votes', withCommas(f.votes));
}
@ -1610,10 +1610,10 @@ if (typeof document !== 'undefined') (function () {
setText('pv-note', (!enabled && pv.default_note) ? pv.default_note + '.' : w.note);
var v = View.verifierWords(pv);
setText('pv-verifier', v.word); $('pv-verifier').className = 'v ' + v.tone;
setText('pv-verifier-help', pv.verifier_note || 'the node checks a proof before it counts');
setText('pv-verifier-help', pv.verifier_note || 'the verifier: the node checks a proof before it counts');
$('pv-setup-row').hidden = !((pv.enabled && !pv.available && pv.setup_hint) || v.needsSetup);
var seg = pv.segments_submitted ? pv.segments_submitted + ' proven whole, ' + (pv.segments_paid || 0) + ' paid' + (pv.segment_paid_wei && Number(pv.segment_paid_wei) ? ' (' + (Number(pv.segment_paid_wei) / 1e18).toFixed(4) + ' IGN)' : '') + (pv.segment_last_s ? ', the last took ' + Math.round(pv.segment_last_s) + ' s' : '') + (pv.segments_held ? ', ' + pv.segments_held + ' waiting for the chain' : '') : 'none yet';
setText('pv-seg', seg); setText('pv-seg-note', pv.segment_note && enabled ? pv.segment_note : 'whole runs of blocks this machine proved');
setText('pv-seg', seg); setText('pv-seg-note', pv.segment_note && enabled ? pv.segment_note : 'whole segments (runs of 8 blocks) this machine proved');
setText('pv-program', pv.program_id || 'not read yet');
setText('pv-aggregator', pv.aggregator_id || 'not read yet');
}

View file

@ -188,11 +188,11 @@
<div class="card dag-card" id="dag-card">
<div class="card-head"><h3>The chain, live</h3><div class="head-right"><span class="eyebrow" id="dag-state">connecting</span><button class="btn tiny ghost" id="dag-pause" aria-pressed="false">Pause</button></div></div>
<div class="dag-wrap">
<canvas id="dag-live" aria-label="The last two minutes of blocks: your row and the other miners, the main chain as a lit path, locks as bands"></canvas>
<canvas id="dag-live" aria-label="The last two minutes of blocks: your row and the other miners, the selected chain as a lit path, checkpoints as bands"></canvas>
<div class="dag-tip mono" id="dag-tip" hidden></div>
<canvas id="dag" aria-hidden="true" hidden></canvas>
</div>
<div class="legend"><span><i class="lg you"></i>yours</span><span><i class="lg pending"></i>pending</span><span><i class="lg included"></i>included</span><span><i class="lg excluded"></i>excluded</span><span><i class="lg proven"></i>proven</span><span><i class="lg locked"></i>locked</span><span class="dim" id="dag-note"></span></div>
<div class="legend"><span><i class="lg you"></i>yours</span><span><i class="lg pending"></i>pending</span><span><i class="lg included"></i>included</span><span><i class="lg excluded"></i>excluded</span><span><i class="lg proven"></i>proven</span><span><i class="lg locked"></i>locked checkpoint</span><span class="dim" id="dag-note"></span></div>
</div>
<div class="card node-card" id="node-card">
@ -212,8 +212,8 @@
<div><span class="k">tips</span><span class="v mono" id="n-tips">0</span><span class="m">open ends of the chain right now</span></div>
<div><span class="k">blue score</span><span class="v mono" id="n-blue">0</span><span class="m">blocks on the agreed main chain (the blue score)</span></div>
<div><span class="k">next program</span><span class="v mono" id="d-eta">--:--</span><span class="m" id="d-eta-sub">the hourly mining program</span></div>
<div><span class="k">last lock</span><span class="v mono" id="f-lock">none yet</span><span class="m" id="f-note">a point the miners agreed can never be undone</span></div>
<div><span class="k">votes sent</span><span class="v mono" id="f-votes">0</span><span class="m">this machine signs a lock point every 30 s</span></div>
<div><span class="k">last checkpoint</span><span class="v mono" id="f-lock">none yet</span><span class="m" id="f-note">a point the miners agreed can never be undone</span></div>
<div><span class="k">votes sent</span><span class="v mono" id="f-votes">0</span><span class="m">this machine signs a checkpoint every 30 s (a point the miners agree can never be undone)</span></div>
</div>
<div class="field">
<div class="k">rules fingerprint</div>
@ -314,7 +314,7 @@
<div class="lead-row">
<div class="lead-text">
<h3>Prove on this machine</h3>
<p class="help">Every block is turned into a short proof. Your cards prove pieces of it and earn IGN for each one.</p>
<p class="help">Every block is turned into a short proof, in pieces called shards. Your cards prove shards and earn IGN for each one.</p>
</div>
<label class="switch lg" title="Prove on this machine"><input type="checkbox" id="s-prove" aria-label="Prove on this machine"><span class="track"></span></label>
</div>
@ -326,16 +326,16 @@
<div class="details" id="pv-details" hidden>
<div class="kv">
<div><span class="k">verifier</span><span class="v" id="pv-verifier">not read yet</span><span class="m" id="pv-verifier-help">the node checks a proof before it counts</span></div>
<div><span class="k">segments</span><span class="v" id="pv-seg">none yet</span><span class="m" id="pv-seg-note">whole runs of blocks this machine proved</span></div>
<div><span class="k">segments</span><span class="v" id="pv-seg">none yet</span><span class="m" id="pv-seg-note">whole segments (runs of 8 blocks) this machine proved</span></div>
</div>
<div class="field">
<div class="k">proof program id</div>
<div class="k">shard program id</div>
<div class="box mono"><span id="pv-program">not read yet</span><button class="btn tiny" data-copy="pv-program">Copy</button></div>
</div>
<div class="field">
<div class="k">combiner program id</div>
<div class="k">aggregator id</div>
<div class="box mono"><span id="pv-aggregator">not read yet</span><button class="btn tiny" data-copy="pv-aggregator">Copy</button></div>
<p class="help">Every proof names the program that made it. Other nodes accept a proof only from these two ids.</p>
<p class="help">Every proof names the program that made it: the shard program for one shard, the aggregator for a whole segment. Other nodes accept a proof only from these two ids.</p>
</div>
</div>
</div>
@ -418,7 +418,7 @@
<details class="card adv" id="s-advanced">
<summary><h3>Advanced</h3><span class="eyebrow">devnet</span></summary>
<label class="switch"><input type="checkbox" id="s-vote"><span class="track"></span><span class="sw-text"><b>Vote on lock points</b> <span class="dim">Signs a lock point every 30 s. Votes are what lock the chain. Leave it on.</span></span></label>
<label class="switch"><input type="checkbox" id="s-vote"><span class="track"></span><span class="sw-text"><b>Vote on checkpoints</b> <span class="dim">Signs a checkpoint every 30 s. Votes are what lock the chain. Leave it on.</span></span></label>
<label class="switch"><input type="checkbox" id="s-trust"><span class="track"></span><span class="sw-text"><b>Trust proofs without checking them</b> <span class="dim">Devnet only. The node includes proofs it never checked. Changing this restarts the node.</span></span></label>
<div class="ask inline" id="ask-trust" hidden>
<span class="ask-text" id="ask-trust-text"></span>

View file

@ -123,7 +123,7 @@ test('the prove words follow the switch, the setup, the node and the status', ()
assert.equal(V.proveWords(pv({ status: 'proving', current: 'block 59199 shard 0' }), true, true).sub, 'block 59199 shard 0');
assert.equal(V.proveWords(pv({ status: 'submitted' }), true, true).tone, 'on');
assert.equal(V.proveWords(pv(), true, true).word, 'idle');
assert.equal(V.verifierWords(pv()).word, 'checking proofs · pool 4/4');
assert.equal(V.verifierWords(pv()).word, 'verifying · pool 4/4');
assert.equal(V.verifierWords(pv()).tone, 'ok');
assert.equal(V.verifierWords(pv({ verifier_mode: 'off', pool_entries: 0 })).tone, 'warn');
assert.equal(V.verifierWords(pv({ verifier_mode: 'off', verifier_reason: 'the WSL2 prover is not installed' })).needsSetup, true);
@ -294,8 +294,8 @@ test('the Tuning card schedule and the goal consequence', () => {
});
test('the prove tier sentence per card, and the counts in one line', () => {
assert.equal(V.proveTier(card()), 'NVIDIA GeForce RTX 5090: proves while it mines (32 GB; a full piece needs 20.4 GB).');
assert.equal(V.proveTier(card({ name: 'NVIDIA GeForce RTX 4070', vram_mb: 12288 })), 'NVIDIA GeForce RTX 4070: cannot prove a full piece (12 GB; a full piece needs 20.4 GB).');
assert.equal(V.proveTier(card()), 'NVIDIA GeForce RTX 5090: proves while it mines (32 GB; a full shard needs 20.4 GB).');
assert.equal(V.proveTier(card({ name: 'NVIDIA GeForce RTX 4070', vram_mb: 12288 })), 'NVIDIA GeForce RTX 4070: cannot prove a full shard (12 GB; a full shard needs 20.4 GB).');
assert.equal(V.proveTier(card({ name: 'Apple M5 Max', vendor: 'apple' })), 'Apple M5 Max: proves on the CPU, slowly.');
assert.equal(V.proveTier(card({ name: 'AMD Radeon RX 9070 XT', vendor: 'amd', vram_mb: 16384 })), 'AMD Radeon RX 9070 XT: cannot prove yet (proving needs an NVIDIA card).');
assert.equal(V.proveLine({ assigned: 0, submitted: 0, paid: 0, paid_wei: '0' }, false, true), 'Off · not proving · 0 assigned · 0 proven · 0 paid · 0.0000 IGN');

View file

@ -5,7 +5,10 @@ the project lead, on the Cards screenshot: "'As the fleet' might need some work,
prior, hill climb, sweep, DAA, epoch, vote key, digest, manifest, shard, segment, aggregator, verifier, worker, engine
become plain words or carry one grey explanation), every state line one short sentence with the number first, labels
are nouns and buttons are verbs, no colon where a sentence will do, no two-beat lines, one word for one thing (tune,
card). the project lead's rulings folded in: the per-card inherit option is "Default · <shared goal>" in grey, updating when the
card). Main's correction (20:0x UK): the chain's own objects keep the words of the site, the litepaper and the ledger,
with a grey explanation where they first appear: shard (not piece), segment (not run of blocks), checkpoint and
"locked checkpoint" (not lock point), aggregator (not combiner), verifying (not checking proofs); "network blocks (the
DAA score)" stays. The rows below that renamed those words are superseded by this correction. the project lead's rulings folded in: the per-card inherit option is "Default · <shared goal>" in grey, updating when the
shared goal changes; "Default" wherever a per-card setting follows the shared one (the cap's "Back to Default"); the
Cards strip is titled "Ember Tune"; every goal option carries its meaning as a tooltip: Efficiency "most hashes per
watt, a little rate traded", Balanced "a little rate for most of the saving", Maximum "most hashes, watts second",
@ -22,14 +25,14 @@ Default "the goal set for all cards". 150 strings changed.
| Cards, the cap lines | cap applied: 460 W / cap not applied: Windows needs the administrator prompt / Unpin | Cap in force: 460 W / Cap not set yet. Windows needs the administrator prompt. / Back to Default |
| Cards, the row's state word | held for a remote job / stopped after repeated faults | paused for a job from the team / stopped after repeated errors |
| Overview, the big button | switch a GPU on below first / no GPU this app can drive / worker starting | switch a card on in Cards first / no card this app can use / card starting |
| Prove | verifying / off: relay only / shard program id / aggregator id / a full shard needs 20.4 GB | checking proofs / off, passing proofs along / proof program id / combiner program id / a full piece needs 20.4 GB |
| Node details | DAA score / rules digest / next rule switch / signs a checkpoint | network blocks (the DAA score) / rules fingerprint / next rule change / signs a lock point |
| Settings | Vote on finality checkpoints / Trust proof records without verifying them / Hill climb | Vote on lock points / Trust proofs without checking them / Fine tuning |
| Prove | off: relay only / Every block is turned into a short proof. The chain hands pieces to your cards | off, passing proofs along / Every block is turned into a short proof, in pieces called shards. Your cards prove shards and earn IGN for each one (shard, segment and aggregator keep their names, each explained once) |
| Node details | DAA score / rules digest / next rule switch / last lock | network blocks (the DAA score) / rules fingerprint / next rule change / last checkpoint (a point the miners agree can never be undone) |
| Settings | Vote on finality checkpoints / Trust proof records without verifying them / Hill climb | Vote on checkpoints / Trust proofs without checking them / Fine tuning |
Every toast and event sentence lost its semicolon or colon joint for a full stop ("Mining stops. The node keeps
running"). "Worker" (the engine's word for a card's mining process) became "card" wherever a user reads it; "engine"
became "app"; "shard" became "piece"; "segment" became "run of blocks"; "checkpoint" became "lock point"; "fleet
prior" became "what other miners found". The engine's own log strings are untouched: `View.plainEvent` maps them.
became "app"; "fleet prior" became "what other miners found". Shard, segment, checkpoint and aggregator stay (the
chain's own words), each with a grey explanation where it first appears. The engine's own log strings are untouched: `View.plainEvent` maps them.
## Every string changed