From b420e67d9f681b44f65c4fd19847c0aa98fe16ce Mon Sep 17 00:00:00 2001 From: J Date: Sun, 18 Oct 2026 16:09:21 +0530 Subject: [PATCH 1/4] fix(track): signal refresh start to live presenter --- src/tasks/WatchTasks.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tasks/WatchTasks.ts b/src/tasks/WatchTasks.ts index 0512aa0c..f0568a70 100644 --- a/src/tasks/WatchTasks.ts +++ b/src/tasks/WatchTasks.ts @@ -205,6 +205,9 @@ export class WatchTasks { return true; } this.children.set(id, child); + // Notify the presenter when a refresh begins, not only when it completes: + // a slow sample must be shown as refreshing while its previous result stays visible. + this.emit(watch); const take = (decoder: StringDecoder, chunk: Buffer) => { const kept = Math.min(chunk.length, Math.max(0, MAX_OUTPUT - receivedBytes)); if (kept) { receivedBytes += kept; output += decoder.write(chunk.subarray(0, kept)); } From 6832fec5ebe4d2e829e822d5b5edd993a3f2ae23 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 18 Oct 2026 16:09:25 +0530 Subject: [PATCH 2/4] fix(track): release stopped tracker presentation state --- src/app/TerminalApp.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/TerminalApp.ts b/src/app/TerminalApp.ts index 0ca8fe6c..2564a507 100644 --- a/src/app/TerminalApp.ts +++ b/src/app/TerminalApp.ts @@ -8694,6 +8694,9 @@ export class TerminalApp { if (block && watch.status === 'stopped' && !block.finalized) { block.finalized = true; this.output.addFrontendBlock(`/track ${watch.command}`, finalResultRows(watch)); + // The anchored transcript row can render its stopped state directly from the task. + // The mutable per-tracker height bookkeeping is no longer needed after finalization. + this.trackBlocks.delete(watch.id); } this.render(); }); From 419748739ff47e509ffb8810153ff491000ceb7c Mon Sep 17 00:00:00 2001 From: J Date: Sun, 18 Oct 2026 16:09:27 +0530 Subject: [PATCH 3/4] test(track): assert finalizer releases presentation state --- tests/trackApp.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/trackApp.test.ts b/tests/trackApp.test.ts index c3834deb..ebcce954 100644 --- a/tests/trackApp.test.ts +++ b/tests/trackApp.test.ts @@ -45,6 +45,7 @@ test('/track in the app: one block whose live result refreshes in place; /track const text = stored(app).join('\n'); assert.match(text, /Stopped · last result \d\d:\d\d:\d\d · exit 0 · \d+ runs?\n\s*two/u); assert.equal(text.split('Stopped · last result').length, 2, 'written once'); + assert.equal(app['trackBlocks'].size, 0, 'a stopped tracker releases its live height bookkeeping'); assert.match(live(app)[0] ?? '', /^■ stopped/u, 'the live rows end with the stopped state'); app['handleWatch']('/watch ls', 'start', 'ls', false); From ed16fa154047a045c982b2c280f437785d484a76 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 18 Oct 2026 16:09:53 +0530 Subject: [PATCH 4/4] test(track): verify refreshing state notifies presenter immediately --- tests/track.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/track.test.ts b/tests/track.test.ts index fb8e3115..6402cadf 100644 --- a/tests/track.test.ts +++ b/tests/track.test.ts @@ -103,6 +103,27 @@ test('/track reuses one current and previous snapshot, does not overlap manual r } }); +test('/track emits a live refresh-start event before the sample completes', async () => { + const cwd = mkdtempSync(join(tmpdir(), 'nmsh-track-refresh-')); + const engine = new WatchTasks('/bin/sh'); + try { + const task = engine.start('sleep 0.2; printf "ready\\n"', cwd, 5000, + {kind: 'allowed', network: false, remote: false}); + await waitFor(() => task.runs === 1); + let notifiedWhileSampling = false; + const unsubscribe = engine.onChange(changed => { + if (changed.id === task.id && engine.sampling(task.id)) notifiedWhileSampling = true; + }); + assert.equal(engine.runNow(task.id), true); + assert.equal(notifiedWhileSampling, true, 'presenter is notified during refresh'); + await waitFor(() => task.runs === 2); + unsubscribe(); + } finally { + engine.dispose(); + rmSync(cwd, {recursive: true, force: true}); + } +}); + test('/track strips terminal control sequences from the displayed snapshot', async () => { const cwd = mkdtempSync(join(tmpdir(), 'nmsh-track-controls-')); const engine = new WatchTasks('/bin/sh');