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: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: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: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
|
- Keep templates simple markdown
|
||||||
- Add/update tests
|
- Add/update tests
|
||||||
|
|
||||||
## TASK-017 — Generate context files from discovery answers
|
## TASK-017 — Support task roles in generated prompts
|
||||||
|
|
||||||
Status: Todo
|
Status: Done
|
||||||
|
|
||||||
Goal:
|
Goal:
|
||||||
Use captured discovery answers to generate initial context files.
|
Allow tasks to define the agent role used by `rdb prompt`.
|
||||||
|
|
||||||
Implementation Gap:
|
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:
|
Acceptance Criteria:
|
||||||
|
|
||||||
- Add command or function to generate context files from discovery data
|
- Add command or function to generate context files from discovery data
|
||||||
|
- Use the approved mapping document
|
||||||
- Create missing context files safely
|
- Create missing context files safely
|
||||||
- Do not overwrite existing files without explicit behaviour
|
- Do not overwrite existing files without explicit behaviour
|
||||||
- Add/update tests
|
- 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
|
Status: Todo
|
||||||
|
|
||||||
|
Role: Implementation Agent
|
||||||
|
|
||||||
Goal:
|
Goal:
|
||||||
|
|
||||||
Report which expected context files exist and which sections still need information.
|
Report which expected context files exist and which sections still need information.
|
||||||
|
|
||||||
Implementation Gap:
|
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:
|
Acceptance Criteria:
|
||||||
|
|
||||||
@@ -319,15 +414,34 @@ Acceptance Criteria:
|
|||||||
- Report obvious placeholder sections still needing completion
|
- Report obvious placeholder sections still needing completion
|
||||||
- Add/update tests
|
- 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
|
Status: Todo
|
||||||
|
|
||||||
|
Role: Documentation Agent
|
||||||
|
|
||||||
Goal:
|
Goal:
|
||||||
|
|
||||||
Create a recommended Claude Code permissions configuration for RDB workflows.
|
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:
|
Acceptance Criteria:
|
||||||
|
|
||||||
- Safe commands auto-approved
|
- Define recommended safe commands for auto-approval
|
||||||
- Dangerous commands require approval
|
- Define commands that should require approval
|
||||||
- Profile documented in CLAUDE.md
|
- 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)
|
TASK_HEADING_RE = re.compile(r"^##\s+(TASK-\d+)\s+[-–—]\s+(.+)$", re.MULTILINE)
|
||||||
STATUS_RE = re.compile(r"^Status:\s*(.+)$", re.MULTILINE)
|
STATUS_RE = re.compile(r"^Status:\s*(.+)$", re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
GOAL_RE = re.compile(r"^Goal:\s*(.+)", re.MULTILINE)
|
GOAL_RE = re.compile(r"^Goal:\s*(.+)", re.MULTILINE)
|
||||||
AC_RE = re.compile(r"^- (.+)$", re.MULTILINE)
|
AC_RE = re.compile(r"^- (.+)$", re.MULTILINE)
|
||||||
GAP_RE = re.compile(r"^Implementation Gap:\s*(.+)", 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
|
@dataclass
|
||||||
class Task:
|
class Task:
|
||||||
task_id: str
|
task_id: str
|
||||||
@@ -20,6 +29,15 @@ class Task:
|
|||||||
status: str
|
status: str
|
||||||
body: 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:
|
def goal(self, root: Path) -> str:
|
||||||
match = GOAL_RE.search(self.body)
|
match = GOAL_RE.search(self.body)
|
||||||
if match:
|
if match:
|
||||||
@@ -167,8 +185,10 @@ def generate_agent_prompt(root: Path) -> str:
|
|||||||
if not task:
|
if not task:
|
||||||
return "No Todo task found."
|
return "No Todo task found."
|
||||||
|
|
||||||
|
role_text, article = task.role(root)
|
||||||
|
|
||||||
sections = [
|
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)
|
commands = _extract_test_commands_from_claude(tmp_path)
|
||||||
assert commands == []
|
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