fix(antigravity): a pending shell approval reads as working, plus a reattach test

Review round: the footer hint is drawn only at rest, so nothing arms the idle check while the Run this command list is up. The stock.ts comment, wiki, changeset and test now say a pending approval shows as working until answered, and the test pins busy during the confirmation. Adds the antigravity copy of the reattached-pane settle test, and updates the promptGlyph comment and the fixture header.
This commit is contained in:
Devvyn committed 2026-10-18 00:45:52 +08:00
1 parent 4a47ead824
commit 0085a75c33
5 files changed
+40 -11

No files matched your search

+33 -7
View File
@@ -1,6 +1,6 @@
/**
* Working/idle detection for an interactive agent pane, Claude's, Codex's, pi's, opencode's, omp's
* and Gemini CLI's.
* Gemini CLI's and Antigravity's.
*
* The bug this pins: Claude redraws the composer (`❯`) about once a second all
* the way through a turn, so the old "saw a ❯, wait 2s, call it idle" rule
@@ -15,8 +15,9 @@
*
* The status-line fixtures below are verbatim captures from live panes
* (`tmux -L codeman capture-pane -p`) on Claude Code 2.1.220, Codex CLI 0.152.1, pi 1.1.0,
* opencode 1.3.0, omp 18.8.6 / 18.0.11 and Gemini CLI 0.63.0 (its turns driven by a local
* stand-in for the Gemini API, since the CLI's look does not depend on the backend).
* opencode 1.3.0, omp 18.8.6 / 18.0.11, Gemini CLI 0.63.0 (its turns driven by a local
* stand-in for the Gemini API, since the CLI's look does not depend on the backend) and
* Antigravity `agy` 1.3.3.
*/
import { describe, expect, it, vi, afterEach } from 'vitest';
import { Session } from '../src/session.js';
@@ -1070,7 +1071,7 @@ describe('antigravity interactive activity', () => {
expect(session.status).toBe('idle');
});
it('reads a pending shell confirmation as idle, and ends the resumed turn too', () => {
it('keeps a pending shell confirmation working until it is answered, and ends the resumed turn too', () => {
vi.useFakeTimers();
let screen = AGY_WORKING;
const session = withFakePane(() => screen, 'antigravity');
@@ -1084,9 +1085,12 @@ describe('antigravity interactive activity', () => {
screen = AGY_CONFIRM;
feed(session, 'Run this command?\r\n');
vi.advanceTimersByTime(10_000);
// The footer hint is not drawn under the confirmation, so nothing armed the check:
// the pane stays working until the user answers and the turn ends below.
vi.advanceTimersByTime(60_000);
// The footer hint is not drawn under the confirmation, so nothing arms the idle check:
// the pane stays working (unlike gemini, whose bar returns on every repaint) until the
// user answers and the turn ends below.
expect(events).toEqual(['working']);
expect(session.status).toBe('busy');
screen = AGY_WORKING;
spin(session, 3000);
screen = AGY_AT_REST;
@@ -1099,6 +1103,28 @@ describe('antigravity interactive activity', () => {
});
});
describe('antigravity reattach', () => {
afterEach(() => {
vi.useRealTimers();
});
// Declaring workDetect turns off the 3 s settle for a restored pane, so a reattached agy pane
// depends on the footer hint arriving in tmux's attach repaint to leave `busy`.
it('settles a reattached antigravity pane that is at rest', () => {
vi.useFakeTimers();
const session = withFakePane(AGY_AT_REST, 'antigravity');
(session as unknown as { _status: string })._status = 'busy';
const events: string[] = [];
session.on('idle', () => events.push('idle'));
feed(session, AGY_TURN_END);
vi.advanceTimersByTime(20_000);
expect(events).toEqual(['idle']);
expect(session.status).toBe('idle');
});
});
describe('wire activity stamp across recovery', () => {
// The stamp both home screens sort the quiet group on. Recovery restores the
// previous run's value, and the settle window keeps the boot attach repaint