Skip to content

fix(subagents): no live subagents can exist at startup - #147

Merged
devsuitup merged 1 commit into
mainfrom
fix/bootstrap-ghost-subagents
Aug 22, 2026
Merged

fix(subagents): no live subagents can exist at startup#147
devsuitup merged 1 commit into
mainfrom
fix/bootstrap-ghost-subagents

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

The bug

Observed on a restart tonight: a subagent that had already finished showed up as running — violet glyph on the parent session, green dot on the subagent group header — and stayed that way for about thirty seconds.

At startup the scanner treated any subagent transcript whose mtime was under a minute old as a live agent, and announced it. The user had simply restarted shortly after an agent finished.

The fix

The argument that justifies the age filter turns around at startup: nothing on disk can be a live agent when the app boots. Switchboard owns the PTYs — one set per Electron process — and they die with it, taking their agents along. A restored session gets a fresh PTY whose agents write after boot and are detected normally, and a claude started outside Switchboard never enters this path at all.

So bootstrap now announces nothing, whatever the mtimes.

But the verdict stays reversible. A first version froze it permanently, which traded a visible thirty-second ghost for a live agent that would have been invisible for the whole session if the ownership argument were ever wrong. Instead, only files with a recent mtime — none or one or two after a restart, never the thousands of history files — get a recheck window. If such a file starts growing, the rehabilitation path added in #142 releases the withheld spawn. If it stays still, nothing was ever announced.

Silence at startup, without the blind spot.

Tests

Four new bootstrap tests, and the two that mattered were verified against the intermediate commit as well as against main: pinning the history/recent distinction, and the full reversible cycle (silence → growth → exactly one late spawn → normal completion). A third pins that the ghost stays gone when nothing moves: three flushes over thirty-one seconds, zero events.

Two existing tests had their setup rewritten — they built their live entry from the synthetic bootstrap spawn, which no longer exists, so they now bootstrap on an empty directory and create the file afterwards. Their assertions are unchanged; one gained an extra assertion. Reviewed line by line.

task check: 671 tests, 664 pass, 0 fail, 7 pre-existing skips, 0 lint errors.

Rationale in .ai/contexts/subagent-observability.md, including why bootstrap needs no age filter and why the recheck window keeps the ownership argument falsifiable.

Bootstrap treated any subagent file written in the last 60 s as a running
agent and emitted a synthetic subagent-spawned. An agent that finished
shortly before a restart therefore reappeared as active — purple activity
glyph on the parent, green dot on the group header — until the 30 s
stability window declared it complete.

The age filter has no basis at bootstrap: Switchboard owns the PTYs its
subagents run in, so they all die with the process. Record every
pre-existing file as finished, with no IPC.

The verdict stays reversible where it could be wrong. Files with a fresh
mtime — the only ones that could conceivably still be running — keep a
recheck window and are announced late if they grow, so an orphaned writer
surviving a hard kill surfaces instead of staying invisible for the whole
session. Plainly historical files are frozen outright, which keeps startup
free of a per-file statSync.

The post-bootstrap path is unchanged.
@devsuitup
devsuitup merged commit 874fa64 into main Aug 22, 2026
7 checks passed
@devsuitup
devsuitup deleted the fix/bootstrap-ghost-subagents branch August 22, 2026 22:27
@devsuitup devsuitup mentioned this pull request Aug 22, 2026
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