From 6abdd554566c8d0f2d2aa5a40bfd7fe8477a7ece Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 13 Nov 2026 17:24:43 -0800 Subject: [PATCH] Exercise predecessor status at its editor boundary Replace a counterfactual shell boundary inside an application screen with recorded editor writes through the actual status producer. Verify stale support rejection, current repaint acceptance, and unattributed fallback. Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> --- .../terminal-predecessor-evidence.test.ts | 55 ++++++++++++------- .../wait/tui-idle-command-paint.test.ts | 31 ----------- 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/src/main/runtime/terminal/wait/terminal-predecessor-evidence.test.ts b/src/main/runtime/terminal/wait/terminal-predecessor-evidence.test.ts index ce519ea48c5..b9cb4e345ac 100644 --- a/src/main/runtime/terminal/wait/terminal-predecessor-evidence.test.ts +++ b/src/main/runtime/terminal/wait/terminal-predecessor-evidence.test.ts @@ -1,3 +1,4 @@ +import { observeFreebuffTerminalStatus } from '../output/freebuff-terminal-status' import { HeadlessEmulator } from '../../../daemon/emulation/headless-emulator' import { readRuntimeFixture } from './agent-transcript-replay-test-harness' import { MAX_TAIL_CHARS } from '../output/tail/terminal-tail-limits' @@ -7,7 +8,6 @@ import { setTerminalLineOrigins, terminalOriginSpan, terminalScreenSupport, - joinedTerminalOrigins, terminalSupportQuery } from '../../../../shared/terminal/output/terminal-content-origin' import { buildVisibleSnapshotReadFallback } from '../output/tail/terminal-tail-read' @@ -23,10 +23,7 @@ import { readWaitBlockedCarry } from './wait-blocked-check-state' import { restoreFromReattachedReadyScreen } from './reattached-ready-screen' -import { - readFreebuffScreenStatus, - FreebuffScreenStatusTracker -} from '../../../../shared/freebuff-screen-status' +import { readFreebuffScreenStatus } from '../../../../shared/freebuff-screen-status' import { TerminalCommandPaint, terminalTitleIsPredecessor } from '../input/terminal-command-paint' import { buildTerminalActionText, readTerminalActionText } from '../output/terminal-action-text' import { extractAllOscTitleEntries } from '../../../../shared/agent-status/title/osc-title-extraction' @@ -156,29 +153,47 @@ it('does not promote a known predecessor screen on reattach and retains unknown expect(applyIdle).toHaveBeenCalledOnce() }) -it('qualifies Freebuff supports before deduplication and keeps unknown grammar', async () => { +it('qualifies edited Freebuff supports before publishing and keeps unknown grammar', async () => { const owner = {} - const scope = { owner, epoch: 2 } + const old = { owner, epoch: 1 } + const current = { owner, epoch: 2 } const recording = readRuntimeFixture('freebuff-ready') const ready = `${recording.slice(recording.indexOf('\x1b[?1049h')).split('\x1b[?2026l')[0]!}\x1b[?2026l` const emulator = new HeadlessEmulator({ cols: 120, rows: 40 }) - let lines: string[] try { - await emulator.write(ready) - lines = emulator.getVisibleLines() + await emulator.write(ready, { contentOrigin: old }) + expect( + observeFreebuffTerminalStatus(emulator, ready, undefined, 'freebuff', old) + ).toMatchObject({ + state: 'done', + sessionBoundary: true + }) + const working = '\x1b[33;1Hworking...' + await emulator.write(working, { contentOrigin: old }) + expect( + observeFreebuffTerminalStatus(emulator, working, undefined, 'freebuff', old) + ).toMatchObject({ + state: 'working' + }) + const erase = '\x1b[33;1H\x1b[2K' + await emulator.write(erase, { contentOrigin: current }) + expect( + observeFreebuffTerminalStatus(emulator, erase, undefined, 'freebuff', current) + ).toBeNull() + expect(readFreebuffScreenStatus(emulator.getVisibleLines(), true)).toMatchObject({ + state: 'done', + sessionBoundary: true + }) + await emulator.write(ready, { contentOrigin: current }) + expect( + observeFreebuffTerminalStatus(emulator, ready, undefined, 'freebuff', current) + ).toMatchObject({ + state: 'done', + sessionBoundary: true + }) } finally { emulator.dispose() } - const old = lines.map((line) => terminalOriginSpan(line.length, { owner, epoch: 1 })) - setTerminalLineOrigins(lines, old) - const joined = joinedTerminalOrigins(lines) - const support = terminalSupportQuery(joined.origins, scope) - const tracker = new FreebuffScreenStatusTracker(true) - expect(tracker.observe('erase', () => ({ lines, alternate: true, support }))).toBeNull() - expect(tracker.observe('paint', () => ({ lines, alternate: true }))).toMatchObject({ - state: 'done', - sessionBoundary: true - }) expect( readFreebuffScreenStatus( ['freebuff found agent files in this repository', 'Load and run these? [y/N]'], diff --git a/src/main/runtime/terminal/wait/tui-idle-command-paint.test.ts b/src/main/runtime/terminal/wait/tui-idle-command-paint.test.ts index 60085c7f0f9..c40586660e7 100644 --- a/src/main/runtime/terminal/wait/tui-idle-command-paint.test.ts +++ b/src/main/runtime/terminal/wait/tui-idle-command-paint.test.ts @@ -315,34 +315,3 @@ it('does not promote a retained Qoder composer after replacement until it is rep vi.useRealTimers() } }) - -it('does not turn old Freebuff ready support into fresh status when only the working row is erased', async () => { - const { runtime, handle } = await createTranscriptPane({ - paneTitle: 'Terminal', - foregroundProcess: 'freebuff', - launchAgent: 'freebuff', - data: '', - size: { cols: 120, rows: 40 } - }) - vi.useFakeTimers() - try { - const write = (data: string) => runtime.onPtyData(TRANSCRIPT_PANE_PTY_ID, data, Date.now()) - const recording = readRuntimeFixture('freebuff-ready') - const ready = `${recording.slice(recording.indexOf('\x1b[?1049h')).split('\x1b[?2026l')[0]!}\x1b[?2026l` - write(`\x1b]133;C\x07\x1b]0;Freebuff: task\x07\x1b[?1049h\x1b[H${ready}`) - await runtime.readTerminal(handle, { screen: true }) - write('\x1b[33;1Hworking...') - await runtime.readTerminal(handle, { screen: true }) - write('\x1b]133;C\x07\x1b[33;1H\x1b[2K') - const settled = vi.fn() - const wait = runtime.waitForTerminal(handle, { condition: 'tui-idle', timeoutMs: 60000 }) - wait.then(settled, () => {}) - await vi.advanceTimersByTimeAsync(6000) - expect(settled).not.toHaveBeenCalled() - write(`\x1b[H${ready}`) - await vi.advanceTimersByTimeAsync(4000) - await expect(wait).resolves.toMatchObject({ satisfied: true }) - } finally { - vi.useRealTimers() - } -})