Skip to content

relay#552: the detached-host journey sweeps its runner on every exit, signals included - #555

Merged
barelyworkingcode merged 4 commits into
epic/525-session-adoptionfrom
fix/552-detached-journey-cleanup
Oct 21, 2026
Merged

barelyworkingcode merged 4 commits into
epic/525-session-adoptionfrom
fix/552-detached-journey-cleanup

Conversation

@barelyworkingcode

Copy link
Copy Markdown
Owner

Closes #552

The session-detached-host journey left its runner, the runner's two helpers and the run folder behind when devboxverify was killed mid-journey. A SIGTERM ended the process with no deferred cleanup. The runner leads its own process group and outlives its caller by design, so nothing else reaps it.

The journey now:

  • records every session it starts, including the ones its start retry loop abandons;
  • runs one sweep on every return path: relay session stop, the agent's process group, every process whose command line names the session's run folder, and the folder itself;
  • catches SIGINT and SIGTERM while it runs, cleans up, then re-raises the same signal so the process still ends;
  • fails the journey (whatever its state) when a process still names a started session's run folder after the sweep, or when the process list can't be read.
    Relay's own stop is still checked first on the PASS path; the sweep runs after it.

Proof, on a stack slot built from this branch (product code identical to the epic head):

  • Repro on the epic head's harness: a failure injected after start and after link loss left nothing, because the old defer covers in-journey returns. A SIGTERM to devboxverify mid-turn left runner.sh and two helpers (parent pid 1) and the run folder. That matches the owner's report.
  • The branch, with the same SIGTERM mid-turn: the journey reports FAIL with "terminated received during the journey; cleanup ran". No process names the run folder, the folder and fixture are gone, and the process ends on the signal.
  • Clean --phase api runs: main's harness passed 6, not-run 2; the branch harness passed 6, not-run 2 (twice, before and after review fixes).
  • Break-and-restore: with the sweep call disabled, the same SIGTERM gives cleanup: pids [...] still name a started session's run directory. Source was restored byte for byte. My runs' leftovers were removed.
    The owner's own stray session from before this work was not touched.

Out of scope, theoretical: a session start that a signal cancels after relay accepted it, so its id never reached the journey.

Fix attempts: 2 (the second added the signal path once the cause was shown) plus one review round of fixes. Tokens: about 600k. Review rounds: 2.

Review: no blocking findings (default model, round 2; round 1 had one blocking finding, fixed: a leftover after BLOCKED now fails)
Red on main: epic-head harness at ba4cae1, SIGTERM to devboxverify mid-turn on stack r552 leaves runner.sh plus two helpers and the run folder
Real-app check: stack r552 (real relay app, loopback host): stray runner seen with the epic-head harness, none with the branch harness
Journey: session-detached-host PASS on the branch harness, --stack --phase api (pass=6 notrun=2)
Harness weakening: none; no check removed, no skip or not-run path widened, no timeout changed; a leftover process now fails a result that passed before
Main harness vs head: main's harness at ba4cae1 against the stack, --phase api: pass=6 fail=0 notrun=2
Break-and-restore: sweep call disabled -> journey FAIL "pids [...] still name a started session's run directory"; restored byte for byte (cmp)

🤖 Generated with Claude Code

barelyworkingcode and others added 4 commits October 20, 2026 17:35
…every exit

A cleanup stops every started session, ends the agent group and the runner processes by run-directory path, removes the directories and fails the result when any remain.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ignalled

A SIGINT or SIGTERM cancels the journey, the sweep and teardown run, then the signal is delivered again so the process still ends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…terministic signal handoff

finish turns PASS, BLOCKED and NOTRUN into FAIL on a leftover or an unreadable process list; the signal goroutine is joined before the final drain; an ignored SIGINT stays ignored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@barelyworkingcode
barelyworkingcode merged commit eac89bc into epic/525-session-adoption Oct 21, 2026
@barelyworkingcode
barelyworkingcode deleted the fix/552-detached-journey-cleanup branch October 21, 2026 00:52
@barelyworkingcode

Copy link
Copy Markdown
Owner Author

merge-check: merged. approved, target, contract, evidence, owner, weakening

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