feat(task-018): add role-based context selection

This commit is contained in:
2026-06-02 18:09:11 +01:00
parent 34f7364c86
commit 1c2faa3162
6 changed files with 197 additions and 23 deletions
+5
View File
@@ -36,3 +36,8 @@
{"timestamp": "2026-06-02T16:29:43.405985+00:00", "event_type": "command", "target": "rdb next", "details": {}} {"timestamp": "2026-06-02T16:29:43.405985+00:00", "event_type": "command", "target": "rdb next", "details": {}}
{"timestamp": "2026-06-02T16:29:59.879010+00:00", "event_type": "command", "target": "rdb prompt", "details": {}} {"timestamp": "2026-06-02T16:29:59.879010+00:00", "event_type": "command", "target": "rdb prompt", "details": {}}
{"timestamp": "2026-06-02T16:47:40.958013+00:00", "event_type": "command", "target": "rdb status", "details": {}} {"timestamp": "2026-06-02T16:47:40.958013+00:00", "event_type": "command", "target": "rdb status", "details": {}}
{"timestamp": "2026-06-02T16:57:02.286495+00:00", "event_type": "command", "target": "rdb status", "details": {}}
{"timestamp": "2026-06-02T16:57:05.774698+00:00", "event_type": "command", "target": "rdb next", "details": {}}
{"timestamp": "2026-06-02T16:57:29.853052+00:00", "event_type": "command", "target": "rdb prompt", "details": {}}
{"timestamp": "2026-06-02T17:05:41.754949+00:00", "event_type": "command", "target": "rdb status", "details": {}}
{"timestamp": "2026-06-02T17:07:48.597850+00:00", "event_type": "command", "target": "rdb status", "details": {}}
+1 -1
View File
@@ -309,7 +309,7 @@ Definition of Done:
## TASK-018 — Role-based context selection ## TASK-018 — Role-based context selection
Status: Todo Status: Done
Role: Implementation Agent Role: Implementation Agent
+49 -13
View File
@@ -11,9 +11,11 @@ Requires-Dist: pytest>=8.0.0; extra == "dev"
# rdb-discovery # rdb-discovery
A small CLI-first tool for repeatable software project discovery and delivery. A small CLI-first tool that helps you generate and maintain useful project context files for AI-assisted development.
It creates markdown files that help humans and AI coding agents understand: ## Purpose
rdb-discovery exists to make it easier for humans and AI coding agents to understand:
- what is being built - what is being built
- why it exists - why it exists
@@ -21,6 +23,20 @@ It creates markdown files that help humans and AI coding agents understand:
- what task should be done next - what task should be done next
- what stage the project is currently in - what stage the project is currently in
The tool creates and manages a set of markdown files — your project's source-of-truth context.
## Core workflow
1. **Ask discovery questions** — `rdb discover` captures answers about the product, architecture, risks, and open questions.
2. **Capture answers** — Structured responses are stored in `context/discovery-log.md` alongside confidence scores and follow-ups.
3. **Generate context files** — `rdb init` creates standardised project files (`product-brief.md`, `architecture.md`, `decisions.md`, etc.).
4. **Support agent implementation** — `rdb prompt` produces ready-to-paste implementation prompts for Claude Code or Cline, and `rdb start` / `rdb complete` manage task lifecycles.
## Support features (not the product)
- **Telemetry** records structured agent activity events in `.rdb/session-log.jsonl`. It enables future analysis but is not the main offering.
- **Guardrails** detect agent stalls, repeated reads, and inconsistent state to keep projects on track. They support the workflow but are secondary.
## Install for local development ## Install for local development
```bash ```bash
@@ -32,23 +48,43 @@ pip install -e '.[dev]'
## Commands ## Commands
```bash ```bash
rdb init rdb init # Create standard project structure and context files
rdb discover rdb discover # Ask 10 core discovery questions, record answers
rdb status rdb guardrails # Check for agent stalls and state inconsistencies
rdb next rdb prompt # Generate implementation prompt for the next task
rdb start TASK-001 rdb start TASK-001 # Mark a task as in progress
rdb complete TASK-001 rdb complete TASK-001# Mark a task as done
rdb handoff rdb status # Show current stage and active task
rdb next # Print the next task to work on
rdb handoff # Generate agent handoff notes
``` ```
## Bootstrap workflow ## Bootstrap workflow
1. Run `rdb init` 1. Run `rdb init`
2. Run `rdb discover` 2. Run `rdb discover`
3. Run `rdb status` 3. Review `rdb status` and `rdb next`
4. Run `rdb next` 4. Give the next prompt to Claude Code or Cline via `rdb prompt`
5. Give the next task to Claude Code or Cline 5. Commit after each completed task
6. Commit after each completed task
## Project structure
`rdb init` creates the following files (skips existing ones):
- context/discovery-log.md
- context/product-brief.md
- context/architecture.md
- context/decisions.md
- context/risks.md
- context/assumptions.md
- context/open-questions.md
- context/repository-context.md
- TASKS.md
- TEST_PLAN.md
- RUN_LOG.md
- PROJECT_STATE.md
- AGENT_HANDOFF.md
- .rdb/project.json
## Principle ## Principle
+7
View File
@@ -3,9 +3,11 @@ pyproject.toml
src/rdb_discovery/__init__.py src/rdb_discovery/__init__.py
src/rdb_discovery/cli.py src/rdb_discovery/cli.py
src/rdb_discovery/discovery.py src/rdb_discovery/discovery.py
src/rdb_discovery/guardrails.py
src/rdb_discovery/handoff.py src/rdb_discovery/handoff.py
src/rdb_discovery/status.py src/rdb_discovery/status.py
src/rdb_discovery/tasks.py src/rdb_discovery/tasks.py
src/rdb_discovery/telemetry.py
src/rdb_discovery/templates.py src/rdb_discovery/templates.py
src/rdb_discovery.egg-info/PKG-INFO src/rdb_discovery.egg-info/PKG-INFO
src/rdb_discovery.egg-info/SOURCES.txt src/rdb_discovery.egg-info/SOURCES.txt
@@ -13,5 +15,10 @@ src/rdb_discovery.egg-info/dependency_links.txt
src/rdb_discovery.egg-info/entry_points.txt src/rdb_discovery.egg-info/entry_points.txt
src/rdb_discovery.egg-info/requires.txt src/rdb_discovery.egg-info/requires.txt
src/rdb_discovery.egg-info/top_level.txt src/rdb_discovery.egg-info/top_level.txt
tests/test_discovery.py
tests/test_guardrails.py
tests/test_prompt.py
tests/test_status.py
tests/test_tasks.py tests/test_tasks.py
tests/test_telemetry.py
tests/test_templates.py tests/test_templates.py
+44 -9
View File
@@ -109,7 +109,7 @@ def update_task_status(root: Path, task_id: str, new_status: str) -> bool:
return False return False
CONTEXT_FILES_TO_READ = [ DEFAULT_CONTEXT_FILES = [
"README.md", "README.md",
"TASKS.md", "TASKS.md",
"PROJECT_STATE.md", "PROJECT_STATE.md",
@@ -117,6 +117,38 @@ CONTEXT_FILES_TO_READ = [
"context/agent-guidelines.md", "context/agent-guidelines.md",
] ]
ROLE_CONTEXT_FILES: dict[str, list[str]] = {
"Architecture Agent": [
"README.md",
"TASKS.md",
"PROJECT_STATE.md",
"context/architecture.md",
"context/product-brief.md",
"context/decisions.md",
],
"Implementation Agent": [
"README.md",
"TASKS.md",
"PROJECT_STATE.md",
"AGENT_HANDOFF.md",
"context/agent-guidelines.md",
"CLAUDE.md",
],
"Documentation Agent": [
"README.md",
"TASKS.md",
"PROJECT_STATE.md",
"AGENT_HANDOFF.md",
"context/agent-guidelines.md",
"TEST_PLAN.md",
],
}
def _context_files_for_role(role: str) -> list[str]:
"""Return the context file list for a given role, falling back to default."""
return ROLE_CONTEXT_FILES.get(role, DEFAULT_CONTEXT_FILES)
TEST_CMD_SECTION_RE = re.compile( TEST_CMD_SECTION_RE = re.compile(
r"^##\s*Test Commands\s*\n([\s\S]*?)(?=^##|\Z)", r"^##\s*Test Commands\s*\n([\s\S]*?)(?=^##|\Z)",
re.MULTILINE, re.MULTILINE,
@@ -186,6 +218,7 @@ def generate_agent_prompt(root: Path) -> str:
return "No Todo task found." return "No Todo task found."
role_text, article = task.role(root) role_text, article = task.role(root)
context_files = _context_files_for_role(role_text)
sections = [ sections = [
f"You are {article} {role_text.lower()}.", f"You are {article} {role_text.lower()}.",
@@ -194,13 +227,15 @@ def generate_agent_prompt(root: Path) -> str:
"", "",
"# Read these files first", "# Read these files first",
"", "",
f"""- README.md ]
- TASKS.md for i, f in enumerate(context_files):
- PROJECT_STATE.md line = f"- {f}"
- AGENT_HANDOFF.md""", if i == len(context_files) - 1:
"", sections.append(line)
"- context/agent-guidelines.md", else:
"", sections.append(line)
sections.extend([
"---", "---",
"", "",
"# Task", "# Task",
@@ -210,7 +245,7 @@ def generate_agent_prompt(root: Path) -> str:
f"Status: {task.status}", f"Status: {task.status}",
"", "",
f"Goal:\n{task.goal(root)}", f"Goal:\n{task.goal(root)}",
] ])
gap = task.implementation_gap(root) gap = task.implementation_gap(root)
if gap: if gap:
+91
View File
@@ -322,3 +322,94 @@ def test_generate_agent_prompt_with_custom_role(tmp_path: Path) -> None:
prompt = generate_agent_prompt(tmp_path) prompt = generate_agent_prompt(tmp_path)
assert "You are a devops engineer." in prompt assert "You are a devops engineer." in prompt
def test_architecture_agent_receives_architecture_context(tmp_path: Path) -> None:
(tmp_path / "TASKS.md").write_text(
"# TASKS\n\n"
"## TASK-001 — First\nStatus: Done\n\n"
"Goal: x.\n\n"
"## TASK-002 — Second\nStatus: Todo\n\n"
"Role: Architecture Agent\n"
"Goal: y.\n",
encoding="utf-8",
)
prompt = generate_agent_prompt(tmp_path)
assert "context/architecture.md" in prompt
assert "context/product-brief.md" in prompt
assert "context/decisions.md" in prompt
def test_implementation_agent_receives_implementation_context(tmp_path: Path) -> None:
(tmp_path / "TASKS.md").write_text(
"# TASKS\n\n"
"## TASK-001 — First\nStatus: Done\n\n"
"Goal: x.\n\n"
"## TASK-002 — Second\nStatus: Todo\n\n"
"Role: Implementation Agent\n"
"Goal: y.\n",
encoding="utf-8",
)
prompt = generate_agent_prompt(tmp_path)
assert "AGENT_HANDOFF.md" in prompt
assert "context/agent-guidelines.md" in prompt
assert "CLAUDE.md" in prompt
def test_documentation_agent_receives_documentation_context(tmp_path: Path) -> None:
(tmp_path / "TASKS.md").write_text(
"# TASKS\n\n"
"## TASK-001 — First\nStatus: Done\n\n"
"Goal: x.\n\n"
"## TASK-002 — Second\nStatus: Todo\n\n"
"Role: Documentation Agent\n"
"Goal: y.\n",
encoding="utf-8",
)
prompt = generate_agent_prompt(tmp_path)
assert "TEST_PLAN.md" in prompt
assert "AGENT_HANDOFF.md" in prompt
def test_context_files_differ_by_role(tmp_path: Path) -> None:
(tmp_path / "TASKS.md").write_text(
"# TASKS\n\n"
"## TASK-001 — First\nStatus: Done\n\n"
"Goal: x.\n\n"
"## TASK-002 — Architect\nStatus: Todo\n\n"
"Role: Architecture Agent\n"
"Goal: y.\n",
encoding="utf-8",
)
prompt = generate_agent_prompt(tmp_path)
# Architecture role should have architecture-specific files
assert "context/architecture.md" in prompt
def test_no_role_uses_default_context(tmp_path: Path) -> None:
"""When no Role is set, the default file list is used (backward compatible)."""
(tmp_path / "TASKS.md").write_text(
"# TASKS\n\n"
"## TASK-001 — First\nStatus: Done\n\n"
"Goal: x.\n\n"
"## TASK-002 — Second\nStatus: Todo\n\n"
"Goal: y.\n",
encoding="utf-8",
)
prompt = generate_agent_prompt(tmp_path)
# Default files must still be present
assert "README.md" in prompt
assert "TASKS.md" in prompt
assert "PROJECT_STATE.md" in prompt
assert "AGENT_HANDOFF.md" in prompt
assert "context/agent-guidelines.md" in prompt