Skip to content

fix(agent-status): hold a waiting reported mid-turn until the turn ends - #671

Merged
DerekMarshall merged 1 commit into
mainfrom
fix/held-waiting
Oct 14, 2026
Merged

DerekMarshall merged 1 commit into
mainfrom
fix/held-waiting

Conversation

@DerekMarshall

Copy link
Copy Markdown
Member

Fixes the bug #670's red cases pin: an agent that reports waiting mid-turn showed the hollow ○ at once, and kept showing it while it carried on working.

The bug

waiting is how a turn ends: the agent stops, and background work will wake it. I reported it when I started a background watch of the rc.2 release run, then kept working in the same turn. The dot went to ○ and stayed there for the rest of the turn. :why-status showed SELF-REPORT status=waiting with fresh pane output.

The fix

The tab follows the agent's turn. The status hooks already report each turn's start (working: UserPromptSubmit, agy's PreInvocation) and its end (done, or Claude Code's waiting). waiting::turn_open_after is the pure rule. A question (blocked) or Claude's idle nudge moves neither way.

The agent's own waiting sent while a turn is open is held (waiting::held_for_turn_end). The dot keeps showing the turn. When the turn's end arrives, the held report becomes the live one and meets that end through the unchanged waiting::meet, as if it had been reported there:

  • Claude Code's Stop with pending work shows waiting, in the agent's words.
  • Claude Code's Stop with nothing pending shows done.
  • codex and agy, whose Stop can't say, absorb that done as before.

A held wait gives way to anything newer:

  • any later report from the agent;
  • Enter in the pane;
  • an agent leaving a shell tab (forget_agent);
  • a closed pane.

Agents without turn hooks are unchanged. An agent whose hooks report no turn start never has an open turn, so its waiting applies at once, as before.

Diagnostics: :why-status shows a held: waiting … shown once this turn ends line with the note, and the last report reads not applied: held until this turn ends. The agent's report_status reply says the same.

Tests

  • test(agent-status): red cases for a waiting reported mid-turn shown at once #670's two red cases are un-ignored and pass. The mid-turn case is run for Claude Code and codex, and asserts the dot reads waiting as the turn ends, not just at the next settle.
  • New: a_held_wait_gives_way_to_anything_newer, covering a turn end with nothing pending, the agent's newer report, and Enter.
  • New: pure tests for turn tracking (prompt, end, question, idle nudge, agy's unnamed events) and for the hold rule.
  • New: a shell_agents unit test that forget_agent drops the turn and any held wait.
  • 11 mutations, one per behaviour. Each failed its test, and each run was confirmed to have run a test, not merely compiled.
  • make check-ci: === GATE: PASS ===, 3,182 library tests.

Size ratchet: apply_status_report hit 101 lines, so record_hook_event was extracted. settle_agent_activity grew past its pin, so forget_closed_agent was extracted; it ends at 139, and the pin drops from 141 to 139.

Docs: docs/AGENT_ORCHESTRATION.md, FEATURES.md, AGENTS.md.

Generated with Claude Code

`waiting` is how a turn ends: the agent stops and background work will
wake it. An agent that reported it mid-turn (starting a background watch,
then carrying on) turned its tab's dot to the hollow `○` at once, and it
stayed there for the rest of the turn while the agent kept working.

The status hooks already report each turn's start (`working`) and end
(`done`, or Claude Code's `waiting`), so the tab now follows the turn
(`waiting::turn_open_after`; a question or Claude's idle nudge moves
neither way). The agent's own `waiting` sent while a turn is open is
held (`waiting::held_for_turn_end`) and becomes the live report when the
turn's end arrives, which then meets it through the unchanged precedence
in `waiting::meet`, as if reported there. Any newer report from the agent,
or Enter in the pane, drops a held wait; an agent leaving a shell tab, or
a closed pane, forgets its turn. An agent whose hooks report no turn
start never has one open, so its `waiting` applies at once as before.

`:why-status` shows a held wait and its note, and the report reads `not
applied: held until this turn ends`.

Un-ignores #670's two red cases; adds give-way, turn-tracking and
`forget_agent` tests. Two functions were extracted to stay inside the
function-length ratchet, and `settle_agent_activity`'s pin drops 141 →
139.

Co-Authored-By: Claude <noreply@anthropic.com>
@DerekMarshall
DerekMarshall merged commit f1ca16a into main Oct 14, 2026
4 checks passed
@DerekMarshall
DerekMarshall deleted the fix/held-waiting branch October 14, 2026 08:13
DerekMarshall added a commit that referenced this pull request Oct 15, 2026
Sets the version to `2.2.0-rc.3`, made with `make release-prep
VERSION=2.2.0-rc.3`. Only `Cargo.toml` and `Cargo.lock` change. As with
the earlier rcs, an rc gets no CHANGELOG section, because the 2.2.0
section covers the whole cycle.

## Why another rc

Five changes merged after the rc.2 release run (`v2.2.0-rc.2` was
published 2026-10-14 07:40 UTC). Cutting 2.2.0 from `main` now would
ship them without a soak:

| PR | Change | Why it should soak |
|---|---|---|
| #670 / #671 | fix(agent-status): a `waiting` reported mid-turn is held
until the turn ends | Changes how the activity dot ranks its sources
against each other |
| #672 | fix(codex): a visible async question earns the red attention
dot | Matches what codex draws on screen, verified against codex
0.162.1's default UI |
| #673 | feat(agent): the working dot breathes in 16 eased steps at 10
Hz | Visual. A working agent's dot now redraws at 10 Hz instead of 4 Hz.
Idle is unchanged. |
| #674 | feat(agent-hud): native strip for Antigravity (agy) tabs | A
new reader over agy's transcript format |

Every open bug issue predates July and none is a regression, so rc.3 is
`main` as it stands.

`main` stays at `2.2.0-rc.3` through the soak, which runs into next
week.

**After merge, on `main`:**
1. `make release-tag VERSION=2.2.0-rc.3`
2. Push the tag. `release.yml` verifies the archives reproduce,
publishes a GitHub pre-release, and dispatches the Homebrew tap and apt.
The crates job publishes `spyc 2.2.0-rc.3` and skips `spyc-vt-sys
0.1.0`, which is unchanged.

**Checks:** `make check` passes (the pre-commit gate), and the binary
reports `spyc 2.2.0-rc.3 (<sha>)`.

Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant