System Information
ralph-tui: 0.12.0
runtime: bun 1.3.14
os: win32 10.0.26200 (x64)
agent: opencode v1.18.5
tracker: beads
global-config: yes
project-config: no
templates: beads-bv.hbs, beads-rust-bv.hbs, beads-rust.hbs, beads.hbs, default.hbs, jira.hbs, json.hbs
bundled-skills: 4
skills-installed: claude:0, opencode:0, droid:0, gemini:0, codex:0, kiro:0, cursor:0, github-copilot:0, kimi:0, pi:0
Also reproduced on Linux (same version, same agent).
Steps to reproduce
- Start a headless run against a beads epic:
ralph-tui run --headless --epic <epic-id>
- Note that stdout carries a live progress log, including streamed agent output under
[AGENT].
- Interrupt the process before the epic finishes (or let it exit early).
- Resume the same session headless:
ralph-tui resume --headless
Expected: the same structured log stream as run --headless — timestamped
[level] [component] lines, with agent output streamed as the iteration progresses.
Actual: two plain lines per iteration and nothing in between. With iterations
that take many minutes, stdout is completely silent for the whole iteration, which
is indistinguishable from a hang. I waited 10+ minutes on two separate machines
before concluding the process was stuck; but it wasn't.
This is a straight divergence between the two headless implementations rather than
a config problem:
run --headless (src/commands/run.tsx:3170) builds a createStructuredLogger()
and handles agent:output (line 3271), streaming every stdout/stderr chunk.
It also handles engine:warning, iteration:retrying, iteration:skipped,
task:selected, task:activated, and task:completed.
resume --headless (src/commands/resume.tsx:485) uses bare console.log and
handles only eight events: engine:started, iteration:started,
iteration:completed, iteration:failed, engine:paused, engine:resumed,
engine:stopped, all:complete. There is no agent:output case at all.
The engine still emits agent:output (src/engine/index.ts:1182) — resume just
never subscribes to it.
Two consequences beyond the missing output:
- Retries, skips, and engine warnings are invisible on a resumed session.
resume doesn't track task:activated / task:completed, so activeTaskIds in
.ralph-tui/session.json isn't maintained the way run maintains it.
Relevant logs / terminal output
`resume --headless`, ~10 minutes of real work, entire stdout:
Resuming Ralph TUI session...
Session: a3c96cf6...
Agent: opencode
Tracker: beads
Progress: 3/95 tasks complete
Iteration: 8
Resumed Ralph. Total tasks: 17
--- Iteration 1: Make Fixture::reset() provably isolate cases before generating any ---
Iteration 1 completed. Task in progress. Duration: 624s
--- Iteration 2: Make Fixture::reset() provably isolate cases before generating any ---
The `Duration: 624s` line is the proof the process was healthy: a single iteration
legitimately took 10.4 minutes, and it emitted nothing at all while running.
For contrast, `run --headless` on the same repo produces a continuous
`[timestamp] [INFO] [agent] ...` stream throughout each iteration.
Configuration (if relevant)
Not config-related — reproduced with the global config only, no project
`config.toml`, on both Windows and Linux.
Additional context
- Not agent-specific. The gap is in the command's event subscription, so any agent
is affected.
- Practical impact: any supervisor that shells out to
resume --headless (CI, a
scheduler) has no way to tell a slow iteration from a wedged one. That's
compounded by AgentPlugin.defaultTimeout being 0 — no timeout — by default
(src/plugins/agents/base.ts:366), so there's no upper bound to fall back on
either.
- Workaround until fixed: ignore stdout and poll liveness out of band (check the
agent process, or .ralph-tui/session.json mtime). Per-iteration logs in
.ralph-tui/iterations/ are only written after the iteration ends
(src/logs/persistence.ts:795), so they don't help mid-iteration.
- Suggested fix: have both commands share one headless event handler instead of
maintaining two, so this can't drift again. Happy to open a PR.
System Information
Steps to reproduce
ralph-tui run --headless --epic <epic-id>[AGENT].ralph-tui resume --headlessExpected: the same structured log stream as
run --headless— timestamped[level] [component]lines, with agent output streamed as the iteration progresses.Actual: two plain lines per iteration and nothing in between. With iterations
that take many minutes, stdout is completely silent for the whole iteration, which
is indistinguishable from a hang. I waited 10+ minutes on two separate machines
before concluding the process was stuck; but it wasn't.
This is a straight divergence between the two headless implementations rather than
a config problem:
run --headless(src/commands/run.tsx:3170) builds acreateStructuredLogger()and handles
agent:output(line 3271), streaming every stdout/stderr chunk.It also handles
engine:warning,iteration:retrying,iteration:skipped,task:selected,task:activated, andtask:completed.resume --headless(src/commands/resume.tsx:485) uses bareconsole.logandhandles only eight events:
engine:started,iteration:started,iteration:completed,iteration:failed,engine:paused,engine:resumed,engine:stopped,all:complete. There is noagent:outputcase at all.The engine still emits
agent:output(src/engine/index.ts:1182) —resumejustnever subscribes to it.
Two consequences beyond the missing output:
resumedoesn't tracktask:activated/task:completed, soactiveTaskIdsin.ralph-tui/session.jsonisn't maintained the wayrunmaintains it.Relevant logs / terminal output
Configuration (if relevant)
Additional context
is affected.
resume --headless(CI, ascheduler) has no way to tell a slow iteration from a wedged one. That's
compounded by
AgentPlugin.defaultTimeoutbeing0— no timeout — by default(
src/plugins/agents/base.ts:366), so there's no upper bound to fall back oneither.
agent process, or
.ralph-tui/session.jsonmtime). Per-iteration logs in.ralph-tui/iterations/are only written after the iteration ends(
src/logs/persistence.ts:795), so they don't help mid-iteration.maintaining two, so this can't drift again. Happy to open a PR.