Status-field awareness: - Replace passes:bool with status field in ralph_progress.py - Six buckets: done, attention, active, in-design, ready, blocked - Dashboard shows ⚠ Attention Required section for to-revise/needs-review - auto skips attention stories by default; --include-revise overrides Agent agnosticism: - --agent codex|claude|openrouter|custom on all run commands - Persistent config in .ralph-tui/agent-config.json via set-agent subcommand - OpenRouter adapter: calls API directly when ralph-tui lacks native plugin - custom: --agent-cmd <script> receives prompt file as $1 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
141 lines
6.1 KiB
Markdown
141 lines
6.1 KiB
Markdown
# 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 <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:
|
|
|
|
```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 <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
|