Skip to content

Run skills in the background, watchable and detachable - #7

Merged
gurnben merged 1 commit into
mainfrom
feat/background-runs
Oct 21, 2026
Merged

gurnben merged 1 commit into
mainfrom
feat/background-runs

Conversation

@gurnben

@gurnben gurnben commented Oct 21, 2026

Copy link
Copy Markdown
Member

Summary

Runs used to live inside the launching terminal: closing it (or an incautious ctrl+c) killed a 10-minute audit, and one attach session owned the only live view. This PR moves every run into a detached worker process with the journal as the event bus:

  • Background launch: run spawns a setsid worker (survives the terminal) and attaches by default; -d/--detach fires and forgets and prints the run id with hints. The worker writes options.json in, and the existing journal (spec, stream, result, pid) out; stop signals (SIGINT/SIGTERM) become context cancellations that flow through the same graceful teardown paths as before (crush cancel endpoint, server shutdown).
  • attach <run>: replays the journal from the start and follows it live through the existing monitor. Ctrl-C / q / d detaches without cancelling — the destructive action is now the explicit one (x twice). Attaching to a finished run replays the full transcript and exits with the run's code. Non-TTY attach degrades to a plain live tail.
  • runs / stop / logs / result: run listing with a state machine (running/done/failed/needs-input/budget-stop/aborted/orphaned via result.json + pid liveness), a graceful SIGINT→SIGTERM→SIGKILL stop ladder, rendered journal transcripts, and stored results. Run ids get random 4-hex suffixes; attach/stop/result accept the suffix or any unambiguous prefix.
  • Output completeness: crush tool_result parts — the actual command/scanner output and the bulk of a run's transcript — now stream as tool_result events (truncated to 6 lines/400 chars), rendered as dim blocks in the monitor and plain output. Journal raw lines no longer duplicate parsed events, so replays render once.
  • --json / --no-monitor keep the synchronous in-process CI semantics; the background path is the interactive default.

Verification

  • Live end to end against the fake harness: detached start → runs shows running → ctrl+c detach with the run surviving → graceful stop writing an aborted result → replay of finished runs → no orphaned workers. A fast-run race (worker finishing before the parent's pid handshake) was found and fixed live.
  • New tests: worker round-trip and failure path in-process (options.json → journal → result → state resolution), journal tailer (replay/follow/partial lines), run-state machine, prefix resolution, id uniqueness, tool_result mapping and truncation.
  • Full suite with race detector, vet, staticcheck clean.

💘 Generated with Crush

Runs used to live inside the launching terminal: closing it (or an
incautious ctrl+c) killed a 10-minute audit, and one attach session
owned the only live view.

Every run now executes in a detached worker process (setsid, survives
the terminal): `run` starts it in the background and attaches by
default, `-d` fires and forgets. The worker owns the run's lifetime,
turns stop signals into the same graceful context-driven teardown an
interactive abort used, and writes the existing journal (spec,
stream, result, pid) that everything else reads.

The journal is the event bus: `attach` replays it from the start and
follows it live — ctrl+c detaches without cancelling (the common case
is now instant and non-destructive), x twice cancels, and attaching to
a finished run replays the whole transcript with its result.
`runs` lists run states (running first, then newest), `stop` cancels
through a SIGINT/SIGTERM/SIGKILL ladder, `logs` renders the
transcript, and `result` prints the stored outcome exiting with the
run's own code. Non-TTY attach degrades to a plain live tail; `--json`
and `--no-monitor` keep the synchronous CI semantics.

The monitor also shows tool results now — crush tool_result parts,
truncated to keep the transcript readable — which are the bulk of a
run's output and were previously invisible. Journal raw lines no
longer duplicate parsed events, so replays render once.

Verified end to end against the fake harness: detached start, live
listing, ctrl+c detach with the run surviving, graceful stop writing
an aborted result, replay of finished runs, and no orphaned workers.
Fifty-five tests (race detector on), vet and staticcheck clean.

💘 Generated with Crush

Assisted-by: Crush:rits/zai-org/glm-5-3
@gurnben
gurnben requested a review from a team as a code owner October 21, 2026 16:14
@gurnben
gurnben merged commit 6ed474a into main Oct 21, 2026
2 checks passed
@gurnben
gurnben deleted the feat/background-runs branch October 21, 2026 16:16
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