Skip to content

relay#528: one file-channel runner for host headless sessions - #539

Merged
barelyworkingcode merged 15 commits into
epic/525-session-adoptionfrom
feat/528-runner
Oct 20, 2026
Merged

barelyworkingcode merged 15 commits into
epic/525-session-adoptionfrom
feat/528-runner

Conversation

@barelyworkingcode

Copy link
Copy Markdown
Owner

Closes #528

Host Claude sessions now run under one file-channel runner by default on Unix hosts. A dropped ssh link or a relay restart no longer ends the agent.

  • internal/sessions/runner/runner.sh: one POSIX sh runner. It speaks the owner's agent tool protocol (inbox/ tmp-then-rename, name-order delivery, the move to sent/ is the receipt) and replaces relay's own in.jsonl runner. It writes out.jsonl unchanged and one events.jsonl line per state change (turn_started, turn_ended, asking, exited), each before its cause.
  • hosts[].detach_sessions absent or true runs the runner; false and Windows hosts use ssh -T. A runner that fails to start ends the start with an error; relay never falls back on its own.
  • A live run from the old layout is stopped at its first resume, after the start-time check, and resumes on its conversation.
  • relay#538 (found while working this issue): the ssh ControlPath budget now counts ssh's 17-byte bind suffix. A stack's config dir is long enough that no host session started, which blocked the journey on a --stack run.
  • session-detached-host moves to the api phase, drops its detach_sessions edit, and runs claude against the stack's local model endpoint through a launcher. Off a stack it reports NOTRUN; no journey calls a hosted model.

Split: local restart survival is relay#536, codex and pi are relay#537 (story unchanged).

Contract amendments (logged on the issue): internal/sessions/hostapi/server.go error text; internal/sshhost/sshhost.go budget and its docs/ssh-hosts.md sentence.

Fix attempts: legacy stop 1; review round 1 fixes 1; mawk test reach 1; journey wait signal 1 (it dropped the link on the host's receipt, before relay held it, and got a correct send_unknown once; it now drops after relay session send returns). Tokens: about 1.6M across subagents.

Minor review notes, not fixed:

  • TestHostRunnerWithoutTmux also matches the outside-sessions probe's tmux list-panes; it passes because the probe's next call lands after the test window. Theoretical flake.

  • docs/testing.md does not yet list mawk as a prerequisite for the e2e suite.

  • runner.sh probes awk -W version relying on the launcher's closed stdin.

  • G11.13 names the start-failure text, which no test asserts; TestHostSessionSSHFallbackWhenDetachOff cannot see the -T flag.

  • Journey: session wait runs without --timeout, so a slow turn fails as a killed CLI rather than relay's timeout state.

Why no feature row moved beyond G11.13, G11.14, G1.40 and new G1.48: those are the rows the runner touches.

Run report: barelyworkingcode/claude-process#79

Review: no blocking findings (round 2 on 44520b2; the journey fix b684ccc reviewed separately, no blocking)
Journey: session-detached-host PASS on 3 consecutive --stack runs at b684ccc, each SUMMARY pass=6 fail=0 blocked=0 notrun=2
Real-app check: PASS at 44520b2 on a stack built from the head: a loopback host session with no detach_sessions kept runner pid 48414 through relay host disconnect; the 200-line turn finished; session events showed each line once; a second message answered on g1 with no resume
Harness weakening: no assertion removed or loosened; one not-run path widened: session-detached-host reports NOTRUN off a stack so no journey calls a hosted model (reported as a gate change)
Main harness vs head: epic base harness (121e6b8) against head 44520b2 on a stack: SUMMARY pass=5 fail=0 blocked=0 notrun=2; product code unchanged since
Break-and-restore: runner moving the message to sent/ before the write turned TestRunnerSendReturnsAfterReceipt red at its receipt assertion; restored byte for byte, green. Dropping -W interactive turned TestHostRunnerLinesFlowUnderMawk red

🤖 Generated with Claude Code

barelyworkingcode and others added 15 commits October 20, 2026 09:54
…nix hosts

The runner (runner.sh) replaces the in.jsonl runner: inbox/ and sent/ with
the move to sent/ as the receipt, events.jsonl event lines, runner.json.
hosts[].detach_sessions defaults to on; false and Windows use ssh -T. A legacy
in.jsonl run is stopped once at resume.

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

The journey drops its detach_sessions edit, seeds the loopback host, project
and agent entry into settings.json, and starts the session by the entry. The
replay_unavailable text names the new default.

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

The execute credential is refused on /ws; the project the journey seeds has
its own token.

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The api phase holds only the execute credential, which /ws refuses. The
journey writes a short-lived proxy credential into the stack's settings.json
and removes it after.

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

The running frame precedes the runner writing the message to the agent; a
drop in that window ends the send unknown.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…etached journey needs a stack model

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… follows relay's own receipt

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

Copy link
Copy Markdown
Owner Author

Full e2e suite at 44520b2: one failure, TestFeatureMapCoverage (the new mawk test had no feature row). b8 adds it to G11.13; TestFeatureMapCoverage now passes. Every other feature test passed (586s). The fix touches only docs/FEATURES.md.

@barelyworkingcode
barelyworkingcode merged commit 472a5d6 into epic/525-session-adoption Oct 20, 2026
@barelyworkingcode
barelyworkingcode deleted the feat/528-runner branch October 20, 2026 18:02
@barelyworkingcode

Copy link
Copy Markdown
Owner Author

merge-check: merged. approved, target, contract, evidence, owner
Report: 2. The gates themselves: skip marker added result{id, stateNotRun (cmd/devboxverify/journey_detached_host.go) [name-based]; 2. The gates themselves: criterion removed A headless local session survives a relay restart. (issue #528); 2. The gates themselves: criterion removed Claude, codex and pi sessions run under the same runner. (issue #528)

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