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
24 changes: 21 additions & 3 deletions .claude/hooks/workflow-gate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
"""
Claude Code PreToolUse Hook: Workflow Enforcement Gate
MAP Workflow Enforcement Gate (PreToolUse Hook)

Provider-agnostic: works with both Claude Code and Codex CLI.

Blocks Edit/Write/MultiEdit outside of Actor-related phases.
Uses step_state.json (orchestrator canonical state) as single source of truth.
Expand All @@ -9,7 +11,7 @@
- Edit allowed during phases: ACTOR, APPLY, TEST_WRITER
- Edit blocked during all other phases (DECOMPOSE, MONITOR, PREDICTOR, etc.)
- Fail-open: missing or unreadable step_state.json → allow
- Always allows: .map/ artifacts, ~/.claude/ memory, non-editing tools
- Always allows: .map/ artifacts, non-editing tools

CONSTRAINTS (from step_state.json):
- scope_glob: restrict edits to matching file patterns
Expand All @@ -31,6 +33,20 @@
# Phases where Edit/Write is expected (Actor applies code)
EDITING_PHASES = {"ACTOR", "APPLY", "TEST_WRITER"}

# Map step IDs (used in subtask_phases parallel dict) to phase names
STEP_ID_TO_PHASE = {
"1.0": "DECOMPOSE",
"1.5": "INIT_PLAN",
"1.55": "REVIEW_PLAN",
"1.56": "CHOOSE_MODE",
"1.6": "INIT_STATE",
"2.2": "RESEARCH",
"2.25": "TEST_WRITER",
"2.26": "TEST_FAIL_GATE",
"2.3": "ACTOR",
"2.4": "MONITOR",
}


def extract_target_file_paths(tool_call: dict) -> list[str]:
"""Extract file paths from tool call payload."""
Expand Down Expand Up @@ -129,9 +145,11 @@ def is_editing_phase(branch: str) -> tuple[bool, Optional[str]]:
return True, None # Corrupt/unreadable → fail-open

# Parallel wave mode: check subtask_phases dict
# Values are step IDs (e.g. "2.3") — translate to phase names before comparing
subtask_phases = state.get("subtask_phases", {})
if subtask_phases:
for phase in subtask_phases.values():
for step_id in subtask_phases.values():
phase = STEP_ID_TO_PHASE.get(step_id, step_id)
if phase in EDITING_PHASES:
return True, None

Expand Down
13 changes: 13 additions & 0 deletions .claude/rules/learned/architecture-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,16 @@
- **Agentic Prompt Emphasis Uniformity** (2026-04-11): In multi-phase agentic prompts, every non-negotiable phase must carry identical emphasis markers (MANDATORY, CRITICAL). Selective marking — applying markers to some phases but not others — implicitly signals that unmarked phases are optional. Under cost or confidence pressure ("tests already passed"), agents skip unmarked phases. [workflow: map-learn-bugfix]

- **Orchestrator Prompts Must Prohibit Direct State File Modification** (2026-04-11): When an orchestrator manages workflow state through a structured file (e.g., step_state.json), the agent prompt must contain an explicit NEVER-MODIFY rule naming the file. Without this rule, agents that encounter API limitations will write directly to the state file as a fallback, bypassing all validation the API maintains. The rule must specify what to do instead: call a specific API function, or stop and ask the user. [workflow: map-learn-bugfix]

- **Provider Install Scope Isolation: Each Variant Self-Contains Its Resource Decisions** (2026-04-20): When implementing a multi-provider installation dispatch (Strategy pattern), each provider's install() method must be fully self-contained — it installs only the resources it owns and never invokes helpers belonging to sibling providers. Caller-level dispatch code that calls shared helpers before or after branching leaks those helpers into all variants, including variants that must not receive those resources. Place every resource-allocation decision inside install(). [workflow: map-efficient]
```python
# WRONG — caller leaks create_map_tools() into CodexProvider
def init(project_path, provider='claude'):
create_map_tools(project_path) # always runs — overwrites for codex too!
_get_provider(provider).install(project_path)

# CORRECT — each provider owns its full installation scope
class CodexProvider(BaseProvider):
def install(self, project_path, **kw):
return create_codex_files(project_path) # handles .map/scripts/ internally
```
26 changes: 26 additions & 0 deletions .claude/rules/learned/security-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Security Patterns (Learned)

<!-- MAP-LEARN: populated by /map-learn. Edit freely, commit with project. -->

- **Security Gate Check Ordering: Blocklist Before Allowlist** (2026-04-20): In security enforcement hooks that combine an allowlist (safe command prefixes) and a blocklist (harmful patterns such as redirects, destructive subcommands), always evaluate the blocklist FIRST, before any allowlist prefix check. Allowlist-first creates a structural bypass: a command that starts with an allowed prefix (e.g., 'git ') is approved before harmful sub-patterns ('>>' redirect, 'git restore', 'sed -i') are ever evaluated. The allowlist should only be consulted after confirming no modifying pattern matched. [workflow: map-efficient]
```python
# WRONG — allowlist-first: 'git restore foo' starts with 'git ', returns False
def command_modifies_files(command: str) -> bool:
for prefix in ALWAYS_ALLOWED_PREFIXES:
if command.startswith(prefix):
return False # exits before modifying-pattern scan!
for pattern in FILE_MODIFYING_PATTERNS:
if re.search(pattern, command):
return True
return False

# CORRECT — blocklist-first: no bypass possible regardless of prefix
def command_modifies_files(command: str) -> bool:
for pattern in FILE_MODIFYING_PATTERNS:
if re.search(pattern, command):
return True
for prefix in ALWAYS_ALLOWED_PREFIXES:
if command.startswith(prefix):
return False
return False
```
14 changes: 14 additions & 0 deletions .claude/rules/learned/testing-strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ paths:
<!-- MAP-LEARN: populated by /map-learn. Edit freely, commit with project. -->

- **Monitor Bugs Must Generate Regression Tests** (2026-03-26): When Monitor (or any review tool) finds a bug, always write a failing test that reproduces the bug BEFORE fixing it, because without a regression test the same bug silently reappears during future refactors. Name tests `test_<function>_<what_was_found>` to serve as living documentation. [workflow: map-learn-improvement]

- **Acceptance Tests Must Assert Observable Side Effects, Not Return Types** (2026-04-20): When testing installation, delivery, or file-writing functions, always assert observable filesystem side effects — specific files exist at correct paths, file content matches expectations, paths that must NOT exist are absent. Never rely on return-value structure alone (counts, dicts). A function can return `{'skills': 5}` while writing to the wrong directory. Include negative assertions for provider isolation (`.claude/` must not exist after codex init). [workflow: map-efficient]
```python
# WEAK — passes even if files written to wrong path
def test_codex_installs_skills(tmp_path):
counts = create_codex_files(tmp_path)
assert counts['skills'] > 0 # wrong-path still passes

# STRONG — asserts actual observable side effects
def test_codex_installs_skills(tmp_path):
create_codex_files(tmp_path)
assert (tmp_path / '.codex' / 'skills' / 'map-plan' / 'SKILL.md').exists()
assert not (tmp_path / '.claude').exists() # negative: provider isolation
```
38 changes: 38 additions & 0 deletions .codex/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# MAP Framework Agents

This project uses the MAP (Monitor-Actor-Predictor) Framework for structured development.

## Prerequisites

**Important:** You must trust this project in Codex settings for project-scoped
configuration to take effect. Without trust, `.codex/` files are ignored.

## Available Agents

| Agent | Role | Invoked By |
|-------|------|-----------|
| researcher | Codebase exploration and context gathering | $map-plan Step 0 |
| decomposer | Task decomposition into atomic subtasks | $map-plan Step 4 |
| monitor | Code review and validation | $map-plan SPEC_REVIEW, $map-efficient |

## Available Skills

| Skill | Purpose |
|-------|---------|
| $map-plan | Plan and decompose complex tasks |
| $map-fast | Quick implementation for small changes |
| $map-check | Quality gates and verification |

## Hooks

MAP uses a workflow gate hook that restricts file-modifying commands during
research and review phases. This prevents accidental edits while exploring.

**Note:** Hooks require `codex_hooks = true` in config.toml and are not
supported on Windows.

## Getting Started

1. Trust this project in Codex settings
2. Type `$map-plan <your task>` to start planning
3. Follow the guided workflow
Loading
Loading