Skip to content

Commit 98375a3

Browse files
forge: settled-frontier + tip-balance summary on the Anchors tab
The Anchors page now opens with a three-card strip above the mark table: - Settled frontier: the highest 'marked' mark (marks are a linear spend prefix), linking its commit — the on-chain tip. - Balance at tip: that mark's output amount (the live unspent UTXO), thousands-grouped sats. - Pending: how many derived-but-unfunded marks sit ahead of the frontier, or "settled" when the trail matches HEAD on-chain. All computed from trail data — no explorer call on render. Matches the existing light GitHub-ish chrome. 134 green (+ a frontier/balance/pending assertion on the funded casey/trail fixture).
1 parent eadd451 commit 98375a3

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

forge/plugin.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ table.marks{width:100%;border-spacing:0;font-size:13px}
543543
table.marks th,table.marks td{padding:8px 12px;border-top:1px solid #d0d7de;text-align:left;vertical-align:top}
544544
table.marks th{border-top:0;background:#f6f8fa;font-size:12px;color:#59636e;font-weight:600}
545545
table.marks code{font-size:12px;word-break:break-all}
546+
.markstats{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 16px}
547+
.mstat{flex:1 1 160px;border:1px solid #d0d7de;border-radius:8px;padding:12px 14px;background:#f6f8fa}
548+
.mstat .k{font-size:12px;color:#59636e;text-transform:uppercase;letter-spacing:.03em}
549+
.mstat .v{font-size:20px;font-weight:600;margin-top:3px;line-height:1.2}
550+
.mstat .v .unit{font-size:13px;font-weight:400;color:#59636e}
551+
.mstat .s{font-size:12px;color:#59636e;margin-top:3px}
546552
.fundbox{border:1px solid #d4a72c66;background:#fff8c5;border-radius:8px;padding:12px 16px;margin-top:16px}
547553
.fundbox pre{background:#ffffffaa;border-radius:6px;padding:12px;overflow-x:auto;font-size:12px}
548554
@media (max-width:640px){
@@ -3049,6 +3055,29 @@ ${issuesScript({ api: `${prefix}/api/repos/${owner}/${name}`, owner, name, base,
30493055
// in order — the trail is a linear spend chain).
30503056
const firstPending = trail.marks.find((m) => m.status !== 'marked') ?? null;
30513057
const latest = trail.marks.at(-1);
3058+
// Settled frontier: marks are a linear spend chain, so the 'marked' ones
3059+
// form a prefix and the highest of them is the on-chain tip. Its output is
3060+
// the live (unspent) UTXO — the trail's current funded balance. Everything
3061+
// after it is derived-but-unfunded (pending), i.e. how far HEAD is ahead
3062+
// of what Bitcoin has settled. All from trail data — no explorer call.
3063+
const markedList = trail.marks.filter((m) => m.status === 'marked');
3064+
const frontier = markedList.at(-1) ?? null;
3065+
const pendingCount = trail.marks.length - markedList.length;
3066+
const groupSats = (n) => String(Math.max(0, Math.floor(Number(n) || 0))).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
3067+
const tipSats = frontier ? groupSats(frontier.amount) : null;
3068+
const statStrip = `<div class="markstats">
3069+
<div class="mstat"><div class="k">Settled frontier</div>
3070+
<div class="v">${frontier
3071+
? `#${frontier.index} <a class="sha" href="${base}/commit/${esc(frontier.state.commit)}">${esc(frontier.state.commit.slice(0, 7))}</a>`
3072+
: '<span class="muted">none yet</span>'}</div>
3073+
<div class="s">${frontier ? `on-chain tip &middot; ${esc(frontier.state.branch)}` : 'genesis not yet funded'}</div></div>
3074+
<div class="mstat"><div class="k">Balance at tip</div>
3075+
<div class="v">${tipSats !== null ? `${tipSats} <span class="unit">sat</span>` : '<span class="muted">—</span>'}</div>
3076+
<div class="s">${frontier ? `live ${esc(trail.chain)} output` : 'unfunded'}</div></div>
3077+
<div class="mstat"><div class="k">Pending</div>
3078+
<div class="v">${pendingCount === 0 ? 'settled &#10003;' : `${pendingCount} <span class="unit">mark${pendingCount === 1 ? '' : 's'}</span>`}</div>
3079+
<div class="s">${pendingCount === 0 ? 'trail matches HEAD on-chain' : 'ahead of the settled frontier'}</div></div>
3080+
</div>`;
30523081
const fundBox = latest && latest.status !== 'marked' ? `<div class="fundbox">
30533082
<h3 style="margin:0 0 4px">Fund this mark <span class="chip chip-pending">pending</span></h3>
30543083
<p class="muted" style="margin:4px 0 8px">Mark #${firstPending.index} is waiting for its on-chain output. Send ${esc(chain)}
@@ -3086,6 +3115,7 @@ ${authBox('Recording a transaction')}
30863115
<p class="muted" style="margin:4px 0 16px">Trail key <code>${esc(trail.pubkeyBase.slice(0, 10))}…</code> (forge-held, ${esc(trail.chain)}) &middot;
30873116
${trail.marks.length} mark${trail.marks.length === 1 ? '' : 's'} &middot; the server derives and records; verification runs
30883117
<b>client-side</b> in the hosted verifier against the chain.</p>
3118+
${statStrip}
30893119
<div class="box"><table class="marks">
30903120
<tr><th>mark</th><th>commit</th><th>state hash</th><th>derived address</th><th>status</th><th>tx</th></tr>
30913121
${rows}

forge/test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,6 +2115,20 @@ describe('forge plugin', () => {
21152115
assert.ok(pitch.includes('Anchoring is not enabled'));
21162116
});
21172117

2118+
it('the marks page summarises the settled frontier, tip balance and pending count', async () => {
2119+
// fixture here: 3 marks — #0 marked (250000 sats @ tip0), #1 and #2 pending.
2120+
const html = await (await fetch(`${base}/forge/casey/trail/marks`)).text();
2121+
assert.ok(html.includes('Settled frontier'), 'frontier stat card present');
2122+
assert.ok(html.includes('Balance at tip'), 'balance stat card present');
2123+
// frontier = the last MARKED mark (#0), linking its commit
2124+
assert.match(html, /Settled frontier<\/div>\s*<div class="v">#0 <a class="sha" href="[^"]*\/commit\/[^"]*">/,
2125+
'frontier reads as mark #0 with its commit link');
2126+
// live tip balance = that mark's output amount, thousands-grouped
2127+
assert.ok(html.includes('250,000 <span class="unit">sat</span>'), 'tip balance in grouped sats');
2128+
// two derived-but-unfunded marks sit ahead of the settled frontier
2129+
assert.ok(html.includes('2 <span class="unit">marks</span>'), 'pending count = marks ahead of the frontier');
2130+
});
2131+
21182132
it('a mainnet chain without allowMainnet refuses to activate, loudly', async () => {
21192133
// activate() is called directly rather than through a second
21202134
// startJss: booting another JSS in-process regenerates the

0 commit comments

Comments
 (0)