Skip to content

fix: stop reporting claude panes done while their subagents run - #389

Merged
matthias-scale merged 5 commits into
mainfrom
fix/claude-detect-vim-and-subagent-claim
Sep 23, 2026
Merged

matthias-scale merged 5 commits into
mainfrom
fix/claude-detect-vim-and-subagent-claim

Conversation

@matthias-scale

Copy link
Copy Markdown
Owner

Herdr reported Claude panes on ub1 as done while the subagents they launched were still working. done is AgentState::Idle plus unseen, so an idle screen is all a pane has once its subagent evidence is lost. Three independent faults were dropping that evidence.

Vim mode hid the live-turn rule

Claude prefixes its status row with the vim mode indicator when vim mode is on, so -- INSERT -- ⏵⏵ bypass permissions on · esc to interrupt never matched live_turn_working, which anchors the row on ^\s*[⏸⏵]. Every Claude pane on ub1 renders that prefix, so a pane mid-turn could read idle. The rule now allows the indicator and stays anchored otherwise.

A cached remote manifest shadowed the bundled rules

ub1 loaded remote:~/.local/state/herdr/agent-detection/remote/claude.toml at version 2026.09.11.1, the same version string as the bundled manifest, and read_remote_manifest only prefers bundled when the remote version is strictly older. The updater reported current and never refetched, so bundled rules the remote copy lacked never ran. The manifest version moves to 2026.09.23.1.

A closing-block subagent claim died on its authority

current_direct_closing_report_subagents() re-checked at read time that the reporting hook authority was still live, but the claim is already validated when the report is applied. Starting another turn retires that authority without clearing the claim, so a parent that reported closing_agents = 1 silently read idle from the next turn onward. Pane w9:p15 was the confirmed case: it carried the token, declared one running agent in its own closing block, and reported done, while a sibling pane with the identical token reported working.

The read-time gate is gone. Because the watchdog was the only thing ageing a claim out and it also bailed on a retired authority, it now arms off that authority's later lifecycle boundary and issues a terminal zero report, so a claim can never outlive its expiry. A report that cannot be bound to the current agent session is still retired whole, count included.

New rule: detect subagents from the /tasks hint

Claude puts · /tasks to see subagents · on the status row only while subagent tasks are running and drops it when the last finishes, which makes it the one control that survives the parent returning to an idle prompt. Confirmed on ub1: fourteen panes with no running subagent never rendered the segment, and a pane holding a live subagent did.

Not changed: background shells

background_shell_working has never matched anything — its regex is broken by the vim prefix, by a comma delimiter, and by end-of-line. Repairing it would be wrong. Claude's counter includes every background shell the session registered, not the ones still running: one ub1 pane reports 74 while the whole host runs 238 bash processes across 19 agents. Matching it would pin nearly every pane to working for good, which is why upstream removed the rule in 987b070. It stays as it was.

Verification

cargo nextest run -E 'test(/detect::manifest|terminal::state/)' — 274 passed. claude_kimi_real_screen_fixtures_classify_idle_and_blocked still passes, so the new rule adds no false positive on the captured ub1 idle screens.

just check-parallel did not complete locally. Two runs stopped on different real-server integration tests, agent_start_stops_retrying_when_the_pane_shell_stays_busy and two_servers_share_groups_route_concurrent_mutations_and_recover_stale_catalogs, both with cleanup watchdog errors and both passing in isolation, on a host at load average 74. Leaving that to CI on a clean runner.

The status row carries a vim mode indicator when vim mode is on, so the
row-anchored working rules could never match. The shell counter is also
closed by a comma or the end of the line, not only by an interpunct.

Bump the manifest version so a cached remote copy published under the
previous version no longer shadows these rules.

Claude-Session: https://claude.ai/code/session_01VWN7qDGntr6KuwPKn8Fi5g
Claude's status row counts every background shell the session ever
registered, not the ones still running: one ub1 pane reports 74 while the
whole host runs 238 bash processes across 19 agents. Teaching the rule to
match would pin nearly every pane to working for good.

Keep the vim mode prefix fix on the turn rule, which tracks a real live
turn, and leave the shell rule exactly as it was.

Claude-Session: https://claude.ai/code/session_01VWN7qDGntr6KuwPKn8Fi5g
Claude puts the /tasks hint on the status row only while subagent tasks
are still running and drops it when the last one finishes, so it is the
one control that survives the parent returning to an idle prompt.

Confirmed on ub1: fourteen panes with no running subagent never render
the segment, and a pane holding a live subagent does.

Claude-Session: https://claude.ai/code/session_01VWN7qDGntr6KuwPKn8Fi5g
@matthias-scale matthias-scale added the claimed:cl-6438c7c3 Herdr PR claim label label Sep 23, 2026
@matthias-scale
matthias-scale merged commit 5261f75 into main Sep 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claimed:cl-6438c7c3 Herdr PR claim label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant