Everything a wrapped command writes to stdout and stderr arrives merged in a single clean_output string, with no way to tell them apart.
$ rune run --json -- bash -c 'echo out; echo err >&2; exit 3' | jq -r .data.clean_output
out
err
This is inherent to running the child on one PTY — there is one stream by construction. It is not a bug in the implementation. But it is a significant limitation for the primary stated use case, it is not listed under Known Limitations in ROADMAP.md or in specs/pty_runner/pty_runner.spec.md, and it is a regression against the baseline rune is meant to improve on:
p = subprocess.run(cmd, capture_output=True, text=True)
p.stdout # separate
p.stderr # separate
An agent triaging a failing build normally starts with "what went to stderr". Through rune, it cannot.
Impact
Measured head-to-head on a realistic triage task, the glue-code difference between rune and plain subprocess.run + a one-line ANSI regex was 5 lines vs 7. The PTY is the real differentiator (the child believes it has a terminal, pagers are disabled, ANSI is pre-stripped) — but losing stream separation cancels a meaningful part of that for exactly the consumer rune is aimed at.
Suggested
Either:
- Add an opt-in mode that spawns the child with a PTY on stdout and a pipe on stderr, returning
clean_stdout/clean_stderr alongside the merged view. This loses "stderr is a tty" for the child, which is why it should be opt-in.
- Or, at minimum, document the merge prominently as a Known Limitation so nobody discovers it while debugging an agent that silently cannot distinguish the two.
Found during an independent adversarial audit of v0.2.1.
Everything a wrapped command writes to stdout and stderr arrives merged in a single
clean_outputstring, with no way to tell them apart.This is inherent to running the child on one PTY — there is one stream by construction. It is not a bug in the implementation. But it is a significant limitation for the primary stated use case, it is not listed under Known Limitations in
ROADMAP.mdor inspecs/pty_runner/pty_runner.spec.md, and it is a regression against the baseline rune is meant to improve on:An agent triaging a failing build normally starts with "what went to stderr". Through rune, it cannot.
Impact
Measured head-to-head on a realistic triage task, the glue-code difference between rune and plain
subprocess.run+ a one-line ANSI regex was 5 lines vs 7. The PTY is the real differentiator (the child believes it has a terminal, pagers are disabled, ANSI is pre-stripped) — but losing stream separation cancels a meaningful part of that for exactly the consumer rune is aimed at.Suggested
Either:
clean_stdout/clean_stderralongside the merged view. This loses "stderr is a tty" for the child, which is why it should be opt-in.Found during an independent adversarial audit of v0.2.1.