From 3cc9d47810862d5adef770f4510ccc019baba83f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:17:13 +0000 Subject: [PATCH] The wallet's History row: a recovery lock reads "finalised (recovery lock)", never plain final (review B F04, the founder's ruling) The node lane's lockKind word for a transaction under a recovery lock ("finalised by a recovery lock") reads on the row as "finalised (recovery lock)" with the why: the surviving majority's lock after a long pause, not a certified checkpoint, not yet verified here. Known-failed first on build-2 (the word fell through to the label); 22 wallet UI tests green. Until the node carries the field the pause word stands (wallet-words-20). Co-Authored-By: Claude Fable 5.1 --- app/igneum-wallet/ui/app.js | 2 ++ app/igneum-wallet/ui/view.test.mjs | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/igneum-wallet/ui/app.js b/app/igneum-wallet/ui/app.js index 8712fca48..4fef2063a 100644 --- a/app/igneum-wallet/ui/app.js +++ b/app/igneum-wallet/ui/app.js @@ -144,6 +144,8 @@ var View = (function () { // section 9 of docs/spec/finality-guarantees.md: a block under a locked checkpoint is finalised even while the network's // finality is paused; the pause is the network's word, never a transaction's case 'finality not active': return { word: 'finalised', tone: 'ink', why: 'under a locked checkpoint, not yet verified here; finality is paused on the network above it' }; + // review B F04: a recovery lock is labelled "recovery", never plain "final" (the node lane's lockKind word) + case 'finalised by a recovery lock': return { word: 'finalised (recovery lock)', tone: 'ink', why: 'under a recovery lock: the surviving majority’s lock after a long pause, not a certified checkpoint; not yet verified here' }; case 'unknown': return { word: 'pending', tone: '', why: 'not in any block the node holds' }; } if (e.finality === 'in_block') return { word: 'included', tone: '', why: 'in ' + blk }; diff --git a/app/igneum-wallet/ui/view.test.mjs b/app/igneum-wallet/ui/view.test.mjs index f8d71f8d2..6e9d453bc 100644 --- a/app/igneum-wallet/ui/view.test.mjs +++ b/app/igneum-wallet/ui/view.test.mjs @@ -107,6 +107,10 @@ test('one state word per row: the wallet\'s verified final wins, failed from the // the network's finality is paused; the pause is the network's state word, never a transaction's. Known-failed first: the // node's "finality not active" word was shown on the row. assert.deepEqual(V.stateWord(e, 'finality not active'), { word: 'finalised', tone: 'ink', why: 'under a locked checkpoint, not yet verified here; finality is paused on the network above it' }); + // review B F04 (the founder, 8 October 2026): a recovery lock is labelled "recovery", never plain "final": the node's word + // for a transaction under one (the node lane's lockKind) reads on the row as finalised by a recovery lock. Known-failed + // first: the word fell through to the label. + assert.deepEqual(V.stateWord(e, 'finalised by a recovery lock'), { word: 'finalised (recovery lock)', tone: 'ink', why: 'under a recovery lock: the surviving majority’s lock after a long pause, not a certified checkpoint; not yet verified here' }); assert.equal(V.stateWord(e, 'unknown').word, 'pending'); // no node word yet (the public RPC, or not asked): the wallet's label assert.deepEqual(V.stateWord(e, ''), { word: 'included', tone: '', why: 'in block 140,262' }); @@ -117,8 +121,8 @@ test('one state word per row: the wallet\'s verified final wins, failed from the assert.deepEqual(V.stateWord(r, ''), { word: 'executed', tone: 'ink', why: 'in block 140,201' }); assert.equal(V.stateWord(entry({ hash: 'proving-1-0', kind: 'proving', finality: 'final', checkpoint: 9 }), '').word, 'finalised'); // never a stronger word than the chain's: the words the row can show are exactly the node's plus failed - const words = new Set(['pending', 'included', 'executed', 'proven', 'finalised', 'failed']); - for (const w of ['pending', 'included', 'executed', 'proven', 'finalised', 'finality not active', 'unknown', '']) assert.ok(words.has(V.stateWord(e, w).word), w); + const words = new Set(['pending', 'included', 'executed', 'proven', 'finalised', 'finalised (recovery lock)', 'failed']); + for (const w of ['pending', 'included', 'executed', 'proven', 'finalised', 'finality not active', 'finalised by a recovery lock', 'unknown', '']) assert.ok(words.has(V.stateWord(e, w).word), w); }); test('the row model: kind word, who, sign, amount, the rows that need the node\'s word', () => {