From 14911aae21ec4975f7a805b91e3c4867cb41974b Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 12 Oct 2026 00:17:32 +0200 Subject: feat: let the operator pause and resume a root's indexing (MNP 6.2) A whole-root walk now checks its root before every file: removed or ejected, it stops instead of reading the drive to the end; paused, it finishes from the hash cache alone. A paused root is not watched, not swept by reconcile and survives a restart (roster), its indexed files stay served. Resume runs the reconcile sweep over the root, so entries and their enrichment are kept. Signed root_pause / root_resume over MNP, loopback routes, CLI `root pause|resume`, a pause button on the indexing dock and pause/resume in the roots table of Group settings. Co-Authored-By: Claude Opus 5.5 --- .../src/meshbay_hub/static/index-dock-model.js | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js b/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js index bd4586b..ed81e7c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js @@ -91,8 +91,9 @@ function loud(job, since, now) { * * `memory` is per group: when the current walk was first seen, whether its row * has been shown (a burst that crossed the threshold stays up until it ends), - * whether the operator hid it, and when it finished. Pure: same arguments, same - * answer, so a render that runs twice does not flash anything twice. + * whether the operator hid it, which walk they paused, and when it finished. + * Pure: same arguments, same answer, so a render that runs twice does not flash + * anything twice. */ export function dockRows(jobs, memory, now) { const next = {}; @@ -105,9 +106,12 @@ export function dockRows(jobs, memory, now) { const since = prev.busy && prev.sig === sig ? prev.since : now; const shown = Boolean(prev.busy && prev.shown) || loud(job, since, now); const hidden = Boolean(prev.busy && prev.hidden); - next[gid] = { busy: true, sig, since, shown, hidden, doneAt: 0 }; - if (shown && !hidden) rows.push({ ...job, state: 'running' }); - } else if (prev.busy && prev.shown && !prev.hidden) { + // The node takes a moment to stop, and may go on to the next root + // queued: only the walk that was paused stays off screen. + const paused = prev.busy && prev.pausedSig === sig ? sig : ''; + next[gid] = { busy: true, sig, since, shown, hidden, pausedSig: paused, doneAt: 0 }; + if (shown && !hidden && !paused) rows.push({ ...job, state: 'running' }); + } else if (prev.busy && prev.shown && !prev.hidden && !prev.pausedSig) { next[gid] = { busy: false, doneAt: now }; rows.push({ ...job, state: 'done' }); } else if (prev.doneAt && now - prev.doneAt < DONE_MS) { @@ -124,3 +128,14 @@ export function hideRow(memory, groupId) { if (!prev || !prev.busy) return memory; return { ...memory, [groupId]: { ...prev, hidden: true } }; } + +/** + * The operator paused the walk this row shows. Its row goes at once rather than + * when the node's next report arrives, and no "finished" follows it — the root + * is not finished, it is paused, and its settings row says so. + */ +export function pauseRow(memory, groupId) { + const prev = memory[groupId]; + if (!prev || !prev.busy) return memory; + return { ...memory, [groupId]: { ...prev, pausedSig: prev.sig } }; +} -- cgit v1.2.3