fix(session): never settle a just-prompted pane idle at launch

40560ace made the 3 s launch settle announce its idle. For a CLI with
work detection that edge could now land in the middle of a turn: a
prompt sent ~2.9 s after launch has not been marked working yet (the
working line goes through the deferred parsers), so the settle called
the pane idle and a send-and-wait registered for that prompt resolved
before the turn even started. Before 40560ace the settle was silent, so
this edge is new.

The settle now also leaves a pane to `_confirmIdle()` when a prompt was
submitted since the timer was armed, the same way it already does for a
pane marked working; that confirmation reads the screen before it ends
the turn. A CLI without work detection still settles: nothing else ever
would.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-09 02:18:26 +02:00
parent b539780f33
commit db168cdbe5
2 changed files with 54 additions and 8 deletions
+40
View File
@@ -430,6 +430,46 @@ describe('external CLI launch settle', () => {
expect(session.isWorking).toBe(false);
});
it('never announces idle for a codex pane prompted before the timer, so a send-and-wait is not ended early', () => {
vi.useFakeTimers();
let screen = CODEX_READY;
const session = withFakePane(() => screen, 'codex');
const events = launch(session);
// A prompt 2 s in: its turn has not been marked working when the timer fires.
vi.advanceTimersByTime(2000);
session.markPromptSubmitted();
screen = CODEX_WORKING;
vi.advanceTimersByTime(1000);
expect(events).toEqual(['needsRefresh']);
expect(session.status).toBe('busy');
for (let i = 0; i < 4; i++) {
feed(session, CODEX_COMPOSER_REPAINT);
vi.advanceTimersByTime(1000);
}
screen = CODEX_FINISHED;
vi.advanceTimersByTime(30_000);
// The turn's own end is the one idle edge.
expect(events.filter((e) => e === 'idle')).toEqual(['idle']);
expect(events[events.length - 1]).toBe('idle');
expect(session.status).toBe('idle');
});
it('still settles a prompted CLI without work detection, which has nothing else to settle it', () => {
vi.useFakeTimers();
const session = new Session({ workingDir: '/tmp', mode: 'opencode' });
const events = launch(session);
vi.advanceTimersByTime(2000);
session.markPromptSubmitted();
vi.advanceTimersByTime(1000);
expect(events).toEqual(['idle', 'needsRefresh']);
});
it('settles a RESTORED pane of a CLI without work detection, without a refetch', () => {
vi.useFakeTimers();
// A Codeman restart re-attaches every surviving pane through startInteractive(),