Each story shows an optional second line: agent · status · worktree path (if not main) · last output summary Sources: git worktree list, agent-config.json, session.json, completionNotes, git log on feat/<id> branch, iteration logs. --compact flag suppresses metadata lines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.8 KiB
US-015 — Ralph: agent-agnostic runner + status-field-aware dashboard
Two tightly coupled improvements to the Ralph workflow tooling:
-
Status-field awareness —
ralph_progress.pycurrently readspasses: true/falseeverywhere. Replace allpassesreferences with the richstatusfield introduced in the prd.json schema migration. Surfaceto-reviseandneeds-reviewstories as an attention list rather than treating them like failed or blocked tasks. -
Agent-agnostic runner — Ralph currently hardcodes
agentPlugin: "codex"and always callsralph-tui run --agent codex. Make the agent selectable per session with first-class support for Claude Code CLI, OpenRouter (unified API covering GPT-4, Mistral, DeepSeek, Llama, etc.), and the existing Codex plugin.
Part 1: Status-field awareness in ralph_progress.py
What to replace
Every occurrence of story.get("passes") in scripts/ralph_progress.py must be replaced with a helper that reads the status field with a passes fallback for backward compatibility:
# Status → done mapping (backward-compat: passes=True counts as done if no status field)
_DONE_STATUSES = {"done"}
_ATTENTION_STATUSES = {"to-revise", "needs-review"}
_ACTIVE_STATUSES = {"in-progress"}
_DESIGN_STATUSES = {"in-design"}
def _is_done(story: dict) -> bool:
status = story.get("status")
if status:
return status in _DONE_STATUSES
return bool(story.get("passes")) # fallback
def _needs_attention(story: dict) -> bool:
return story.get("status") in _ATTENTION_STATUSES
def _is_active(story: dict) -> bool:
return story.get("status") in _ACTIVE_STATUSES
def _is_in_design(story: dict) -> bool:
return story.get("status") in _DESIGN_STATUSES
_story_sets rewrite
Current behaviour lumps to-revise into the "done" bucket (because passes=True) or the "open" bucket. New behaviour:
done → status == "done" (or passes=True fallback)
attention → status in {to-revise, needs-review} ← new bucket
active → status == "in-progress"
in-design → status == "in-design" (treated as blocked — needs human before agent)
ready → status == "open" AND all deps done
blocked → status == "open" AND some dep not done
_deps_done rewrite
Dependency is satisfied when the dep story _is_done() — not when passes=True.
Dashboard additions
Ralph progress: Distributed Inference Network
PRD: .scratch/distributed-inference-network/prd.json
[############################] 13/15 complete (86%)
Done: 13 Ready: 1 Attention: 2 Blocked: 0
⚠ Attention required (review before running):
⚠ US-002 02 — Two-node shard pipeline
Wire format replaced by US-011 binary protocol; verify tests.
⚠ US-005 05 — OpenAI-compatible gateway
Gateway orchestration superseded by US-014; defer until US-014 lands.
→ US-014 14 — Tracker-as-first-layer-node (inference entry point)
auto command behaviour
auto skips to-revise and needs-review stories — they need human review before an agent re-runs them. Add --include-revise flag to override this for unattended runs.
_review_report additions
Include a dedicated Attention Required section in the generated review brief listing all to-revise and needs-review stories with their status_reason.
Part 2: Agent-agnostic runner
Agent selection
Add --agent option with these values:
| Value | CLI invoked | Config required |
|---|---|---|
codex |
ralph-tui run --agent codex ... |
OPENAI_API_KEY (existing) |
claude |
ralph-tui run --agent claude ... |
ANTHROPIC_API_KEY |
openrouter |
ralph-tui run --agent openrouter ... |
OPENROUTER_API_KEY + --model |
custom |
--agent-cmd <path> |
user-defined |
If ralph-tui does not natively support a requested agent plugin, ralph_progress.py falls back to invoking the agent CLI directly with the task prompt file, bypassing ralph-tui run entirely.
Persistent agent config
Save the last-used agent choice to .ralph-tui/agent-config.json so it doesn't have to be passed on every command:
{
"agent": "openrouter",
"model": "anthropic/claude-opus-4",
"updatedAt": "2026-06-29T..."
}
--agent on the CLI always overrides the saved config. ralph_progress.py set-agent --agent openrouter --model openai/gpt-4o writes the config file.
OpenRouter adapter
When agent == "openrouter" and ralph-tui doesn't natively support it, ralph_progress.py implements a thin adapter:
- Read the task prompt from the issue file + prd.json story
- POST to
https://openrouter.ai/api/v1/chat/completionswithOPENROUTER_API_KEY - Stream response to stdout
- Watch prd.json for
statuschange to detect task completion - Timeout after configurable duration (default: 10 minutes per task)
The OpenRouter model is set via --model (e.g. openai/gpt-4o, mistralai/mistral-large, meta-llama/llama-3-70b-instruct). Default: anthropic/claude-opus-4.
custom agent
--agent custom --agent-cmd ./my-agent.sh — Ralph passes the task prompt file path as $1. The script exits 0 on success. This makes Ralph compatible with any future agent tool without code changes.
Acceptance Criteria
- All
passesreads inralph_progress.pyreplaced with_is_done()helper (with fallback) _story_setsreturns five buckets: done, attention, active, in-design, ready, blocked- Dashboard shows
⚠ Attention requiredsection withstatus_reasonfor each affected story autoskipsto-revise/needs-reviewby default;--include-reviseoverridesralph_progress.py set-agent --agent <name>writes.ralph-tui/agent-config.json--agent codex|claude|openrouter|customaccepted by all subcommands that invoke Ralphralph_progress.py run-next --agent openrouter --model openai/gpt-4oruns a task via OpenRouter adapterralph_progress.py run-next --agent custom --agent-cmd ./my-agent.shruns a task via custom scriptpython -m pytestpasses from repo root- Commit only this story's changes
Part 4: Per-story metadata in dashboard
Every story in the dashboard gets an optional second line showing what is known about it:
✓ US-012 12 — Real PyTorch model backend
codex · done · "Added model_backend.py with bitsandbytes NF4 support"
⚡ US-014 14 — Tracker-as-first-layer-node
codex · in-progress · worktree: ../AI-worktree-us-014
→ US-015 15 — Ralph agent-agnostic runner
claude · in-progress · worktree: ../AI-worktree-us-015
Sources
| Data | Source |
|---|---|
| Active worktree path | git worktree list --porcelain — match branches feat/<story-id> |
| Agent (active story) | .ralph-tui/agent-config.json → agent field |
| Agent (completed story) | .ralph-tui/session.json → agentPlugin; or completionNotes text |
| Last output summary | story.completionNotes (done); latest git log -1 --format=%s feat/<id> (worktree); last lines of most recent .ralph-tui/iterations/<hash>_<date>_<id>.log (in-progress) |
Behaviour
- Metadata line is omitted if there is nothing to show (no agent, no worktree, no notes)
- Default: shown (verbose).
--compactflag suppresses it for a tighter one-line-per-story view _active_worktrees() -> dict[story_id, rel_path]helper, called once per dashboard render_story_meta(story, worktrees, session) -> str | Nonehelper returns the joined metadata string
Additional acceptance criteria
ralph_progress.py showdisplays worktree path for any story whose branch exists asfeat/<story-id>- Agent name appears next to in-progress and recently-completed stories
completionNotesfrom prd.json appears as the summary for done stories--compactsuppresses metadata lines