feat: support role-based agent prompts
This commit is contained in:
@@ -20,3 +20,19 @@
|
||||
{"timestamp": "2026-06-02T16:13:04.692285+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:13:54.568414+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:15:17.596114+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:19:12.880619+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:19:17.322970+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:21.115719+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:26.933534+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:36.969679+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:40.245143+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:42.679647+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:25:52.729189+00:00", "event_type": "command", "target": "rdb prompt", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:27:38.835967+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:27:46.509757+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:28:22.926960+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:28:27.925682+00:00", "event_type": "command", "target": "rdb next", "details": {}}
|
||||
{"timestamp": "2026-06-02T16:29:40.982618+00:00", "event_type": "command", "target": "rdb status", "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:47:40.958013+00:00", "event_type": "command", "target": "rdb status", "details": {}}
|
||||
|
||||
@@ -282,34 +282,129 @@ Acceptance Criteria:
|
||||
- Keep templates simple markdown
|
||||
- Add/update tests
|
||||
|
||||
## TASK-017 — Generate context files from discovery answers
|
||||
## TASK-017 — Support task roles in generated prompts
|
||||
|
||||
Status: Todo
|
||||
Status: Done
|
||||
|
||||
Goal:
|
||||
Use captured discovery answers to generate initial context files.
|
||||
Allow tasks to define the agent role used by `rdb prompt`.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
Discovery questions can collect information, but the answers are not yet transformed into structured project context documents.
|
||||
Tasks can now include a `Role:` field, but `rdb prompt` still always generates prompts beginning with `You are an implementation agent.`
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
- Parse optional `Role:` field from task markdown
|
||||
- If `Role:` exists, use it in the generated prompt opening
|
||||
- If `Role:` is missing, default to `Implementation Agent`
|
||||
- Add/update tests
|
||||
- Do not change task execution behaviour
|
||||
|
||||
Definition of Done:
|
||||
|
||||
- `Role: Architecture Agent` generates `You are an architecture agent.`
|
||||
- Tasks without a role still generate implementation prompts
|
||||
- Tests pass
|
||||
|
||||
## TASK-018 — Role-based context selection
|
||||
|
||||
Status: Todo
|
||||
|
||||
Role: Implementation Agent
|
||||
|
||||
Goal:
|
||||
|
||||
Allow `rdb prompt` to select different context files based on task role.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
Tasks can now define a role, but every generated prompt still asks the agent to read the same set of files regardless of task type.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
- Architecture Agent receives architecture-focused context
|
||||
- Implementation Agent receives implementation-focused context
|
||||
- Documentation Agent receives documentation-focused context
|
||||
- If no role exists, use current default file list
|
||||
- Add/update tests
|
||||
|
||||
Definition of Done:
|
||||
|
||||
- Context files differ by role
|
||||
- Existing prompts remain backward compatible
|
||||
- Tests pass
|
||||
|
||||
## TASK-019 — Define discovery-to-context mappings
|
||||
|
||||
Status: Todo
|
||||
|
||||
Role: Architecture Agent
|
||||
|
||||
Goal:
|
||||
|
||||
Define how discovery answers should be transformed into project context files.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
Discovery answers are collected and stored, but there is no documented mapping between discovery questions and the context files they should populate.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
- Every discovery question maps to one or more context files
|
||||
- Every discovery question maps to a specific section within those files
|
||||
- Mapping is documented in markdown
|
||||
- Mapping is understandable by future agents
|
||||
- No context generation implementation yet
|
||||
- Add/update tests if required
|
||||
|
||||
Definition of Done:
|
||||
|
||||
- Mapping document exists
|
||||
- Mapping covers all discovery questions
|
||||
- Future implementation work is clearly defined
|
||||
|
||||
## TASK-020 — Generate context files from discovery answers
|
||||
|
||||
Status: Todo
|
||||
|
||||
Role: Implementation Agent
|
||||
|
||||
Goal:
|
||||
|
||||
Generate initial context files using the approved discovery-to-context mapping.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
Discovery answers can be collected, but they are not yet transformed into structured project documentation.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
- Add command or function to generate context files from discovery data
|
||||
- Use the approved mapping document
|
||||
- Create missing context files safely
|
||||
- Do not overwrite existing files without explicit behaviour
|
||||
- Add/update tests
|
||||
|
||||
## TASK-018 — Add context completeness report
|
||||
Definition of Done:
|
||||
|
||||
- Context files can be generated from discovery data
|
||||
- Existing files are preserved
|
||||
- Tests pass
|
||||
|
||||
## TASK-021 — Add context completeness report
|
||||
|
||||
Status: Todo
|
||||
|
||||
Role: Implementation Agent
|
||||
|
||||
Goal:
|
||||
|
||||
Report which expected context files exist and which sections still need information.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
There is no way to see whether a project has enough context for an AI agent to work effectively.
|
||||
There is no way to determine whether a project has sufficient context for effective AI-assisted development.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
@@ -319,15 +414,34 @@ Acceptance Criteria:
|
||||
- Report obvious placeholder sections still needing completion
|
||||
- Add/update tests
|
||||
|
||||
## TASK-019 — Generate Claude Code permission profile
|
||||
Definition of Done:
|
||||
|
||||
- Context report command exists
|
||||
- Missing context is reported clearly
|
||||
- Tests pass
|
||||
|
||||
## TASK-022 — Generate Claude Code permission profile
|
||||
|
||||
Status: Todo
|
||||
|
||||
Role: Documentation Agent
|
||||
|
||||
Goal:
|
||||
|
||||
Create a recommended Claude Code permissions configuration for RDB workflows.
|
||||
|
||||
Implementation Gap:
|
||||
|
||||
Claude Code permission settings are not currently documented, resulting in unnecessary approval prompts and inconsistent behaviour between projects.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
- Safe commands auto-approved
|
||||
- Dangerous commands require approval
|
||||
- Profile documented in CLAUDE.md
|
||||
- Define recommended safe commands for auto-approval
|
||||
- Define commands that should require approval
|
||||
- Document the profile in CLAUDE.md
|
||||
- Include rationale for each category
|
||||
|
||||
Definition of Done:
|
||||
|
||||
- Permission profile documented
|
||||
- CLAUDE.md updated
|
||||
|
||||
@@ -7,12 +7,21 @@ import re
|
||||
TASK_HEADING_RE = re.compile(r"^##\s+(TASK-\d+)\s+[-–—]\s+(.+)$", re.MULTILINE)
|
||||
STATUS_RE = re.compile(r"^Status:\s*(.+)$", re.MULTILINE)
|
||||
|
||||
|
||||
GOAL_RE = re.compile(r"^Goal:\s*(.+)", re.MULTILINE)
|
||||
AC_RE = re.compile(r"^- (.+)$", re.MULTILINE)
|
||||
GAP_RE = re.compile(r"^Implementation Gap:\s*(.+)", re.MULTILINE)
|
||||
|
||||
|
||||
def _indefinite_article(word: str) -> str:
|
||||
"""Return 'a' or 'an' based on the first letter of word."""
|
||||
if not word:
|
||||
return "an"
|
||||
first = word[0].lower()
|
||||
if first in "aeiou":
|
||||
return "an"
|
||||
return "a"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Task:
|
||||
task_id: str
|
||||
@@ -20,6 +29,15 @@ class Task:
|
||||
status: str
|
||||
body: str
|
||||
|
||||
def role(self, root: Path) -> tuple[str, str]:
|
||||
if "Role:" not in self.body:
|
||||
return ("Implementation Agent", _indefinite_article("Implementation Agent"))
|
||||
parts = self.body.split("Role:", 1)
|
||||
value = parts[1].splitlines()[0].strip()
|
||||
if value:
|
||||
return (value, _indefinite_article(value))
|
||||
return ("Implementation Agent", _indefinite_article("Implementation Agent"))
|
||||
|
||||
def goal(self, root: Path) -> str:
|
||||
match = GOAL_RE.search(self.body)
|
||||
if match:
|
||||
@@ -167,8 +185,10 @@ def generate_agent_prompt(root: Path) -> str:
|
||||
if not task:
|
||||
return "No Todo task found."
|
||||
|
||||
role_text, article = task.role(root)
|
||||
|
||||
sections = [
|
||||
"You are an implementation agent.",
|
||||
f"You are {article} {role_text.lower()}.",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
|
||||
@@ -259,3 +259,66 @@ def test_extract_test_commands_no_section(tmp_path: Path) -> None:
|
||||
|
||||
commands = _extract_test_commands_from_claude(tmp_path)
|
||||
assert commands == []
|
||||
|
||||
|
||||
def test_generate_agent_prompt_with_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 — Second\nStatus: Todo\n\n"
|
||||
"Role: Architecture Agent\n"
|
||||
"Goal: y.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
prompt = generate_agent_prompt(tmp_path)
|
||||
|
||||
assert "You are an architecture agent." in prompt
|
||||
|
||||
|
||||
def test_generate_agent_prompt_default_role_when_missing(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"
|
||||
"Goal: y.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
prompt = generate_agent_prompt(tmp_path)
|
||||
|
||||
assert "You are an implementation agent." in prompt
|
||||
|
||||
|
||||
def test_generate_agent_prompt_with_empty_role_defaults(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:\n"
|
||||
"Goal: y.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
prompt = generate_agent_prompt(tmp_path)
|
||||
|
||||
assert "You are an implementation agent." in prompt
|
||||
|
||||
|
||||
def test_generate_agent_prompt_with_custom_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 — Second\nStatus: Todo\n\n"
|
||||
"Role: DevOps Engineer\n"
|
||||
"Goal: y.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
prompt = generate_agent_prompt(tmp_path)
|
||||
|
||||
assert "You are a devops engineer." in prompt
|
||||
|
||||
Reference in New Issue
Block a user