Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/agents/evaluator.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ model: sonnet # Evaluation requires nuanced judgment for trade-off analysis and
# 2026-04-28: high effort — weighted scoring across multiple dimensions
# benefits from extra deliberation budget.
effort: high
disallowedTools:
- Edit
- Write
- Agent
version: 3.1.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ model: sonnet # Balanced: quality validation requires good reasoning
# 2026-04-28: high effort — Monitor's adversarial-review quality scales
# with effort more than with raw model strength.
effort: high
disallowedTools:
- Edit
- Agent
version: 2.10.1
last_updated: 2026-05-27
---
Expand Down
4 changes: 4 additions & 0 deletions .claude/agents/predictor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
name: predictor
description: Predicts consequences and dependency impact of changes (MAP)
model: sonnet # Impact analysis requires complex reasoning - upgraded from haiku
disallowedTools:
- Edit
- Write
- Agent
version: 3.3.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/research-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ description: Heavy codebase reading with compressed output. Use PROACTIVELY befo
# benefits more from latency and parallelism than from reasoning depth.
# Frees Opus/Sonnet budget for the load-bearing decision agents.
model: haiku
disallowedTools:
- Edit
- Agent
version: 1.1.0
last_updated: 2026-04-28
---
Expand Down
10 changes: 9 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,8 @@ If you forked the skill-backed `/map-efficient` workflow, you must manually inte
- Specific, actionable feedback
- Checks against project coding standards

**Capability Constraints (frontmatter):** `disallowedTools: [Edit, Agent]`. Monitor is a read-only reviewer — it may not edit files or spawn sub-agents. Write is permitted for `.map/` evidence artifacts (e.g., review bundles). Task-decomposer separately enforces `permissionMode: plan`.

### 4. Predictor

**Responsibility:** Analyze change impact across codebase.
Expand Down Expand Up @@ -1390,6 +1392,8 @@ If you forked the skill-backed `/map-efficient` workflow, you must manually inte

**Model Used:** Sonnet (impact analysis requires complex reasoning)

**Capability Constraints (frontmatter):** `disallowedTools: [Edit, Write, Agent]`. Predictor is analysis-only — it reads and reports, never edits or spawns sub-agents.

### 5. Evaluator

**Responsibility:** Score solution quality on multiple dimensions.
Expand Down Expand Up @@ -1421,6 +1425,8 @@ If you forked the skill-backed `/map-efficient` workflow, you must manually inte

**Model Used:** Sonnet (evaluation requires nuanced judgment)

**Capability Constraints (frontmatter):** `disallowedTools: [Edit, Write, Agent]`. Evaluator is scoring-only — it reads and scores, never edits or spawns sub-agents.

### 6. Reflector

**Responsibility:** Extract lessons from successes and failures.
Expand Down Expand Up @@ -1519,7 +1525,9 @@ If you forked the skill-backed `/map-efficient` workflow, you must manually inte
- Returns confidence score for search completeness
- Enables Actor to Read() only necessary files

**Model Used:** Sonnet (requires understanding code semantics)
**Model Used:** Haiku (read-mostly; latency + parallelism > reasoning depth)

**Capability Constraints (frontmatter):** `disallowedTools: [Edit, Agent]`. ResearchAgent is read-only — it may not edit files or spawn sub-agents. Write is permitted for the MAP-planning research artifact append mode (`Research_Findings_v1_0` block).

**Usage Context:** Called by Actor when implementing features that integrate with existing code

Expand Down
4 changes: 4 additions & 0 deletions src/mapify_cli/templates/agents/evaluator.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ model: sonnet # Evaluation requires nuanced judgment for trade-off analysis and
# 2026-04-28: high effort — weighted scoring across multiple dimensions
# benefits from extra deliberation budget.
effort: high
disallowedTools:
- Edit
- Write
- Agent
version: 3.1.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions src/mapify_cli/templates/agents/monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ model: sonnet # Balanced: quality validation requires good reasoning
# 2026-04-28: high effort — Monitor's adversarial-review quality scales
# with effort more than with raw model strength.
effort: high
disallowedTools:
- Edit
- Agent
version: 2.10.1
last_updated: 2026-05-27
---
Expand Down
4 changes: 4 additions & 0 deletions src/mapify_cli/templates/agents/predictor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
name: predictor
description: Predicts consequences and dependency impact of changes (MAP)
model: sonnet # Impact analysis requires complex reasoning - upgraded from haiku
disallowedTools:
- Edit
- Write
- Agent
version: 3.3.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions src/mapify_cli/templates/agents/research-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ description: Heavy codebase reading with compressed output. Use PROACTIVELY befo
# benefits more from latency and parallelism than from reasoning depth.
# Frees Opus/Sonnet budget for the load-bearing decision agents.
model: haiku
disallowedTools:
- Edit
- Agent
version: 1.1.0
last_updated: 2026-04-28
---
Expand Down
4 changes: 4 additions & 0 deletions src/mapify_cli/templates_src/agents/evaluator.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ model: sonnet # Evaluation requires nuanced judgment for trade-off analysis and
# 2026-04-28: high effort — weighted scoring across multiple dimensions
# benefits from extra deliberation budget.
effort: high
disallowedTools:
- Edit
- Write
- Agent
version: 3.1.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions src/mapify_cli/templates_src/agents/monitor.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ model: sonnet # Balanced: quality validation requires good reasoning
# 2026-04-28: high effort — Monitor's adversarial-review quality scales
# with effort more than with raw model strength.
effort: high
disallowedTools:
- Edit
- Agent
version: 2.10.1
last_updated: 2026-05-27
---
Expand Down
4 changes: 4 additions & 0 deletions src/mapify_cli/templates_src/agents/predictor.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
name: predictor
description: Predicts consequences and dependency impact of changes (MAP)
model: sonnet # Impact analysis requires complex reasoning - upgraded from haiku
disallowedTools:
- Edit
- Write
- Agent
version: 3.3.1
last_updated: 2026-05-27
---
Expand Down
3 changes: 3 additions & 0 deletions src/mapify_cli/templates_src/agents/research-agent.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ description: Heavy codebase reading with compressed output. Use PROACTIVELY befo
# benefits more from latency and parallelism than from reasoning depth.
# Frees Opus/Sonnet budget for the load-bearing decision agents.
model: haiku
disallowedTools:
- Edit
- Agent
version: 1.1.0
last_updated: 2026-04-28
---
Expand Down
126 changes: 126 additions & 0 deletions tests/test_agent_frontmatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,129 @@ def test_frontmatter_extraction_logic(self):
"""
match = re.match(r"^---\n(.*?)\n---", malformed, re.DOTALL)
assert match is None


class TestAgentCapabilityHardening:
"""Regression guard for issue #378: disallowedTools frontmatter on non-writer agents.

Agents that never legitimately edit code must declare disallowedTools at the
harness level, not rely on prompt-text alone. This test pins those contracts
so they cannot be silently removed.
"""

_REPO_ROOT = Path(__file__).parent.parent
_AGENTS_DIR = _REPO_ROOT / ".claude" / "agents"

def _load_frontmatter(self, agent_name: str) -> dict:
path = self._AGENTS_DIR / f"{agent_name}.md"
assert path.exists(), f"Agent file not found: {path}"
content = path.read_text(encoding="utf-8")
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
assert match, f"No frontmatter in {path}"
return yaml.safe_load(match.group(1))

def test_monitor_has_edit_in_disallowed_tools(self):
fm = self._load_frontmatter("monitor")
disallowed = fm.get("disallowedTools", [])
assert "Edit" in disallowed, (
"monitor must deny Edit — it is a READ-ONLY reviewer"
)

def test_monitor_has_agent_in_disallowed_tools(self):
fm = self._load_frontmatter("monitor")
disallowed = fm.get("disallowedTools", [])
assert "Agent" in disallowed, (
"monitor must deny Agent — it must not spawn sub-agents"
)

def test_monitor_write_not_denied(self):
"""monitor IS allowed to Write — it writes .map/ evidence artifacts."""
fm = self._load_frontmatter("monitor")
disallowed = fm.get("disallowedTools", [])
assert "Write" not in disallowed, (
"monitor needs Write for .map/ evidence artifacts — do not deny it"
)

def test_research_agent_has_edit_in_disallowed_tools(self):
fm = self._load_frontmatter("research-agent")
disallowed = fm.get("disallowedTools", [])
assert "Edit" in disallowed, (
"research-agent must deny Edit — it is a read-only context scanner"
)

def test_research_agent_has_agent_in_disallowed_tools(self):
fm = self._load_frontmatter("research-agent")
disallowed = fm.get("disallowedTools", [])
assert "Agent" in disallowed, (
"research-agent must deny Agent — it must not spawn sub-agents"
)

def test_research_agent_write_not_denied(self):
"""research-agent IS allowed to Write — MAP-planning integration appends to research artifact."""
fm = self._load_frontmatter("research-agent")
disallowed = fm.get("disallowedTools", [])
assert "Write" not in disallowed, (
"research-agent needs Write for MAP-planning research artifact append"
)

def test_predictor_has_edit_in_disallowed_tools(self):
fm = self._load_frontmatter("predictor")
disallowed = fm.get("disallowedTools", [])
assert "Edit" in disallowed, (
"predictor must deny Edit — it is an analysis-only agent"
)

def test_predictor_has_write_in_disallowed_tools(self):
fm = self._load_frontmatter("predictor")
disallowed = fm.get("disallowedTools", [])
assert "Write" in disallowed, (
"predictor must deny Write — it is an analysis-only agent"
)

def test_predictor_has_agent_in_disallowed_tools(self):
fm = self._load_frontmatter("predictor")
disallowed = fm.get("disallowedTools", [])
assert "Agent" in disallowed, (
"predictor must deny Agent — it must not spawn sub-agents"
)

def test_evaluator_has_edit_in_disallowed_tools(self):
fm = self._load_frontmatter("evaluator")
disallowed = fm.get("disallowedTools", [])
assert "Edit" in disallowed, (
"evaluator must deny Edit — it is a scoring-only agent"
)

def test_evaluator_has_write_in_disallowed_tools(self):
fm = self._load_frontmatter("evaluator")
disallowed = fm.get("disallowedTools", [])
assert "Write" in disallowed, (
"evaluator must deny Write — it is a scoring-only agent"
)

def test_evaluator_has_agent_in_disallowed_tools(self):
fm = self._load_frontmatter("evaluator")
disallowed = fm.get("disallowedTools", [])
assert "Agent" in disallowed, (
"evaluator must deny Agent — it must not spawn sub-agents"
)

def test_task_decomposer_has_permission_mode_plan(self):
fm = self._load_frontmatter("task-decomposer")
assert fm.get("permissionMode") == "plan", (
"task-decomposer must keep permissionMode: plan"
)

def test_actor_has_no_disallowed_tools(self):
"""actor is a legitimate writer and must NOT be capability-restricted."""
fm = self._load_frontmatter("actor")
assert "disallowedTools" not in fm, (
"actor is a legitimate writer — do not add disallowedTools"
)

def test_final_verifier_has_no_disallowed_tools(self):
"""final-verifier is a legitimate writer and must NOT be capability-restricted."""
fm = self._load_frontmatter("final-verifier")
assert "disallowedTools" not in fm, (
"final-verifier is a legitimate writer — do not add disallowedTools"
)