Skip to content

app: leave without waiting for crew member opens - #2016

Merged
AbirAbbas merged 1 commit into
app/v3from
fix/app-leave-with-crews
Oct 28, 2026
Merged

AbirAbbas merged 1 commit into
app/v3from
fix/app-leave-with-crews

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

From Santosh's feedback: "cmd q quit seems to take a really long time". Found while measuring quits on a real crew.

What was wrong (app mode)

  • A crew member still opening held up quitting. Quit's optional file cleanup (closeReferences, reapBlank) took every agent's gate. A crew member whose open was waiting on a slow or missing host welcome owned its gate, so Close waited on it, past the desktop's stop. Reproduced on Linux by withholding the member host's welcome: both app/v3 and this branch's base stayed alive until a diagnostic SIGQUIT. The goroutine dump shows Close in detachReferences on the member's gate, which the crew-hold goroutine owns while waiting in remote.Client.attach.
  • Closing stdin armed the leave bound only after the backend lock. The first SIGTERM arms the watchdog at once, but EOF went through Close, which took b.mu first. A held lock left an EOF-only leave unbounded. This matters on Windows, where the desktop can only close stdin.

What changes

  • runApp's existing cancellation is stored on the backend and invoked as Close's first step, before b.mu. EOF, shutdown and the first signal now arm the same watchdog. Signals are still counted, so a second SIGTERM still ends the process at once.
  • After the existing parallel release of held conversations, optional reference and blank-folder cleanup run only for agents whose gate is free. A pending open keeps its files.

markQuit, interruption, release, final saves, service cleanup and the overall leave allowance keep their order and size. The terminal is unchanged.

Manual verification (Linux, real binaries, isolated homes, loopback stub)

Stalled member host: app/v3 and the unfixed branch needed SIGQUIT after 2 s for both EOF and SIGTERM. The fixed binary exits 0 in 4 ms on EOF and 27 ms on SIGTERM; the member's transcript and queued task stay on disk.

Matrix, 3 trials each, EOF only and SIGTERM only: no chats, idle chat, working chat, one held crew, crew member mid-turn, question-blocked chat.

  • Fixed: 36/36 clean exits within 2 s.
  • app/v3: 34/36; two runs stalled past 3.7 s and 5 s.
  • Quit/resume records (resume ids, partial replies, user text, both crew transcripts) were checked in all 70 graceful trials.

The Windows quit kill is fixed by #2009, not by this PR

On Windows every quit used to end in "codeaf did not end; killed (SIGKILL) after 7.0 s". A goroutine dump from a diagnostic Windows build showed Close waiting on a release, which waited on cellstore.Recorder.Drain for the final capture of a workspace that was the whole user profile. #2009 (now on app/v3) seals only the chat's own folder for home and drive-root workspaces. Re-measured on Windows with an engine built from app/v3 d902739, using the same crew/home-folder home: stdin close → exit in 119–161 ms, and a desktop quit of a home-folder chat ends in 0.1 s. This PR stays for what #2009 does not cover: the stdin watchdog gap and a crew member's pending open.

Checks

gofmt, go vet ./cmd/codeaf, and named tests: appcrewleave_test.go (the leave bound with the backend lock held, a pending crew open skipped by cleanup with its files kept) plus the neighbouring quit/close tests. One adversarial review: merge-ready. CI (ci.yml, ci-full.yml) has been dispatched; it is judged against an app/v3 Full baseline.

Merge order: after #2009. Keep #2009's final-save defer, its fresh save contexts and its appLeaveWait + 3*cellstore.SealWithin; this change only moves when the watchdog is armed and narrows optional cleanup. It also stops quit cleanup waiting behind the host-welcome stall that #2014 bounds.

🤖 Generated with Claude Code

@AbirAbbas
AbirAbbas force-pushed the fix/app-leave-with-crews branch 2 times, most recently from eb162d2 to e0d6430 Compare October 28, 2026 20:41
Quitting's optional file cleanup took every agent's gate, so a crew member
still opening (its host's welcome slow or missing) held the engine past the
desktop's stop. Clean up only conversations whose gate is free; a pending
open keeps its files. Held conversations keep the quit and resume road.

Closing the app's stdin now arms the leave bound at Close entry, before the
backend lock, as the first SIGTERM does; before, EOF armed it only after
taking that lock.

Co-Authored-By: Claude Opus 5.5 (1M context) <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