# US-015 — Ralph: agent-agnostic runner + status-field-aware dashboard Two tightly coupled improvements to the Ralph workflow tooling: 1. **Status-field awareness** — `ralph_progress.py` currently reads `passes: true/false` everywhere. Replace all `passes` references with the rich `status` field introduced in the prd.json schema migration. Surface `to-revise` and `needs-review` stories as an attention list rather than treating them like failed or blocked tasks. 2. **Agent-agnostic runner** — Ralph currently hardcodes `agentPlugin: "codex"` and always calls `ralph-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: ```python # 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 ` | 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: ```json { "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: 1. Read the task prompt from the issue file + prd.json story 2. POST to `https://openrouter.ai/api/v1/chat/completions` with `OPENROUTER_API_KEY` 3. Stream response to stdout 4. Watch prd.json for `status` change to detect task completion 5. 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 `passes` reads in `ralph_progress.py` replaced with `_is_done()` helper (with fallback) - `_story_sets` returns five buckets: done, attention, active, in-design, ready, blocked - Dashboard shows `⚠ Attention required` section with `status_reason` for each affected story - `auto` skips `to-revise` / `needs-review` by default; `--include-revise` overrides - `ralph_progress.py set-agent --agent ` writes `.ralph-tui/agent-config.json` - `--agent codex|claude|openrouter|custom` accepted by all subcommands that invoke Ralph - `ralph_progress.py run-next --agent openrouter --model openai/gpt-4o` runs a task via OpenRouter adapter - `ralph_progress.py run-next --agent custom --agent-cmd ./my-agent.sh` runs a task via custom script - `python -m pytest` passes 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/` | | 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/` (worktree); last lines of most recent `.ralph-tui/iterations/__.log` (in-progress) | ### Behaviour - Metadata line is omitted if there is nothing to show (no agent, no worktree, no notes) - Default: shown (verbose). `--compact` flag 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 | None` helper returns the joined metadata string ### Additional acceptance criteria - `ralph_progress.py show` displays worktree path for any story whose branch exists as `feat/` - Agent name appears next to in-progress and recently-completed stories - `completionNotes` from prd.json appears as the summary for done stories - `--compact` suppresses metadata lines