Files
neuron-tai/.scratch/distributed-inference-network/issues/15-ralph-agent-agnostic-status-aware.md
Dobromir Popov 8ea70ff6a0 docs(us-015): add Part 4 — per-story agent/worktree/summary in dashboard
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>
2026-06-29 16:21:57 +03:00

7.8 KiB

US-015 — Ralph: agent-agnostic runner + status-field-aware dashboard

Two tightly coupled improvements to the Ralph workflow tooling:

  1. Status-field awarenessralph_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:

# 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:

  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 <name> 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/<story-id>
Agent (active story) .ralph-tui/agent-config.jsonagent field
Agent (completed story) .ralph-tui/session.jsonagentPlugin; 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). --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/<story-id>
  • 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