Why: Turn the approved multi-CLI routing design into independently testable Cursor implementation units without another manual plan gate. Evidence: The plan self-review found R1-R10 coverage, stable interfaces, five review-focus tests, and no placeholders or whitespace errors. Impact: Authorizes U1-U5 execution through fresh Cursor Agent runs while the host retains scope, verification, integration, and delivery control. Refs: docs/superpowers/specs/2026-10-15-project-agent-execution-design.md
23 KiB
Project Agent Execution Routing Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Build a project-local, Markdown-configured execution router that delegates repository-writing work to ordered Agent CLI profiles and authorizes host-model fallback only after every candidate is safely unavailable.
Architecture: A small agent-exec CLI is the external seam. It parses one agent-execution block from AGENT-EXECUTION.md, validates profiles against built-in or controlled custom adapters, runs each implementation attempt in a fresh detached worktree, and emits a bounded JSON receipt plus a host-owned transport commit. The host remains responsible for scope review, authoritative verification, integration, commits, PRs, CI, merge, and cleanup.
Tech Stack: Python 3 standard library, Git worktrees and plumbing commands, unittest, existing make test-governance discovery, Markdown with a constrained YAML subset.
Spec: docs/superpowers/specs/2026-10-15-project-agent-execution-design.md
Global Constraints
- Preserve goal-first delivery, strict no-scope-expansion, usable-first sequencing, evidence provenance, platform admission, and high-consequence dual keys.
- Use no new third-party runtime dependency. The parser accepts only the documented YAML subset.
- Every implementation task below uses one fresh Cursor CLI run. Cursor workers do not commit, push, open PRs, merge, deploy, or clean canonical worktrees.
- The host creates canonical commits only after inspecting actual changes and running the task's authoritative tests.
run-everythingremoves per-command prompts inside the isolated unit; it never expands user authorization or grants canonical Git authority.model: autoomits the model flag instead of passing the stringauto.- Shell command templates, secret values, unknown placeholders, and implicit environment inheritance are rejected.
failed-unknown,scope-expansion,invalid-config, and safety failures never authorize host fallback.- A normal worker return is bounded by
summary_bytes; raw logs remain outside the repository.
Review Focus
- A CLI exits nonzero after touching files: preserve evidence, prove exact cleanup, then classify
failed-clean; otherwise returnfailed-unknown. - A custom adapter places a placeholder inside a larger argv string: reject it instead of interpolating or invoking a shell.
- A role lists several missing or incompatible profiles: exhaust them in order and issue exactly one
host-fallback-authorizedreceipt. - A worker creates untracked or binary files: include them in the host-owned transport and changed-path inventory without modifying the canonical checkout.
- A worker changes HEAD,
.git, a disallowed path, or an external object: fail-stop and retain the attempt for reconciliation.
File Map
| Path | Responsibility |
|---|---|
AGENT-EXECUTION.md |
Human-readable project configuration and the single machine-readable agent-execution block |
scripts/agent_exec_config.py |
Markdown extraction, constrained YAML parsing, schema validation, and immutable configuration types |
scripts/agent_exec_adapters.py |
Built-in adapter registry, custom adapter validation, capability probes, and argv construction |
scripts/agent_exec.py |
validate/run CLI, selection state machine, detached worktrees, process supervision, transport commits, receipts, cleanup, and host fallback authorization |
tests/test_agent_exec_config.py |
Configuration parser and schema contract |
tests/test_agent_exec_adapters.py |
Adapter argv, model, permission, output, and custom-adapter contract |
tests/test_agent_exec.py |
Real temporary-Git execution, fallback, scope, transport, recovery, timeout, redaction, and process lifecycle |
AGENTS.md |
Short context pointer to AGENT-EXECUTION.md; no duplicated CLI mappings |
policy/delivery.md |
Project-level agent-per-unit execution and fallback authority |
policy/governance.md |
Receipts, isolation, evidence ownership, and recovery rules |
policy/adoption.md |
Adoption status and current capability limit |
policy/tool-adaptation-matrix.md |
Manual/enforced capability classification and verification entrypoints |
scripts/task.sh |
New task-card execution routing and receipt fields |
tests/test_task.py |
Generated task-card contract |
Makefile |
agent-exec-validate target and help text |
.task/feature-goal-first-delivery.md |
Scope, Cursor attempt receipts, stage state, and exact evidence references |
Task 1: U1 Configuration Contract
Files:
- Create:
AGENT-EXECUTION.md - Create:
scripts/agent_exec_config.py - Create:
tests/test_agent_exec_config.py - Modify:
Makefile
Interfaces:
-
Produces:
ExecutionConfig,ExecutionPolicy,Profile,CustomAdapter,ConfigError,load_config(repo: Path) -> ExecutionConfig,validate_config(repo: Path) -> list[str] -
Consumes: Python 3 standard library only
-
Step 1: Write the failing parser and schema tests
Create tests/test_agent_exec_config.py with temporary repository fixtures and these cases:
def test_loads_single_agent_execution_block(): ...
def test_rejects_missing_or_duplicate_blocks(): ...
def test_rejects_unknown_schema_and_unknown_keys(): ...
def test_role_order_does_not_follow_profile_order(): ...
def test_model_auto_is_preserved_as_none(): ...
def test_rejects_unknown_profile_reference(): ...
def test_rejects_shell_command_and_secret_shaped_values(): ...
def test_custom_placeholders_must_fill_whole_argv_items(): ...
def test_numeric_limits_are_positive_and_bounded(): ...
- Step 2: Run the parser tests red
Run: python3 -m unittest tests.test_agent_exec_config -v
Expected: FAIL because scripts.agent_exec_config does not exist.
- Step 3: Implement immutable configuration types
In scripts/agent_exec_config.py, define:
@dataclass(frozen=True)
class ExecutionPolicy:
fresh_process_per_unit: bool
permission: str
fallback_to_host: str
max_parallel: int
timeout_seconds: int
summary_bytes: int
raw_output_bytes: int
@dataclass(frozen=True)
class Profile:
name: str
adapter: str
executable: str
model: str | None
enabled: bool
@dataclass(frozen=True)
class CustomAdapter:
name: str
base_argv: tuple[str, ...]
run_everything_argv: tuple[str, ...]
workspace_argv: tuple[str, ...]
model_argv: tuple[str, ...]
prompt_transport: str
output_format: str
@dataclass(frozen=True)
class ExecutionConfig:
schema: str
execution: ExecutionPolicy
roles: Mapping[str, tuple[str, ...]]
profiles: Mapping[str, Profile]
custom_adapters: Mapping[str, CustomAdapter]
model: auto becomes None. Reject unknown keys, duplicate mapping keys, tabs, anchors, aliases, tags, multiline scalars, implicit dates, shell metacharacter templates, credential-looking keys/values, and placeholders other than the four spec placeholders.
- Step 4: Implement the constrained YAML parser
Support two-space-indented mappings, inline scalar lists, booleans, integers, null, and quoted/unquoted safe strings. The parser does not implement general YAML. Extract exactly one fenced block whose opening marker is ```agent-execution and whose closing marker is ```.
- Step 5: Add the initial project configuration
Create AGENT-EXECUTION.md using the spec's example with role order:
implementation: cursor -> antigravity -> claude -> codex -> pi -> kimi -> opencode
repair: cursor -> antigravity -> codex -> claude
test: pi -> antigravity -> codex
review: claude -> codex -> pi
Set permission: run-everything, fallback_to_host: all-unavailable, model: auto for every profile, and no custom adapters.
- Step 6: Add validation entrypoint
Add make agent-exec-validate, running:
python3 scripts/agent_exec.py validate --repo .
In U1 the target runs python3 -m unittest tests.test_agent_exec_config -v. Task 3 replaces that recipe with the final agent_exec.py validate command.
- Step 7: Run U1 green
Run: python3 -m unittest tests.test_agent_exec_config -v
Expected: PASS with all parser and schema cases.
- Step 8: Host integration commit
The Cursor worker leaves its tree uncommitted. The host inspects changed paths, runs the same test, and commits only the four U1 paths with subject Add project execution agent configuration and the repository-required four body sections.
Task 2: U2 Adapter Seam
Files:
- Create:
scripts/agent_exec_adapters.py - Create:
tests/test_agent_exec_adapters.py - Modify:
scripts/agent_exec_config.py
Interfaces:
-
Consumes:
Profile,CustomAdapter,ExecutionPolicy -
Produces:
AdapterSpec,AdapterProbe,Invocation,probe_adapter(profile, config) -> AdapterProbe,build_invocation(profile, config, workspace, unit_packet, result_path) -> Invocation -
Step 1: Write adapter contract tests red
Cover Cursor, agy, Claude, Codex, PI, Kimi, OpenCode, and custom profiles:
def test_cursor_run_everything_and_auto_model_argv(): ...
def test_antigravity_run_everything_argv(): ...
def test_claude_run_everything_argv(): ...
def test_codex_uses_noninteractive_workspace_sandbox(): ...
def test_pi_and_kimi_auto_model_omit_model_flag(): ...
def test_missing_opencode_is_unavailable(): ...
def test_fixed_model_uses_only_adapter_model_flag(): ...
def test_custom_adapter_builds_direct_argv_without_shell(): ...
def test_unverified_run_everything_capability_is_unavailable(): ...
- Step 2: Run adapter tests red
Run: python3 -m unittest tests.test_agent_exec_adapters -v
Expected: FAIL because the adapter registry does not exist.
- Step 3: Implement adapter types and registry
Define:
@dataclass(frozen=True)
class AdapterSpec:
name: str
noninteractive_argv: tuple[str, ...]
run_everything_argv: tuple[str, ...]
workspace_flag: str | None
model_flag: str | None
output_argv: tuple[str, ...]
prompt_transport: str
@dataclass(frozen=True)
class AdapterProbe:
available: bool
reason: str | None
executable: str
observed_version: str | None
@dataclass(frozen=True)
class Invocation:
argv: tuple[str, ...]
cwd: Path
stdin: bytes | None
sanitized_display: tuple[str, ...]
Use direct argv with no shell. Probe via executable lookup plus bounded --version/--help inspection. Do not contact a provider during validate.
- Step 4: Implement built-in mappings
Map the currently observed CLI interfaces from the spec. Cursor uses sandboxed --force; Codex uses exec, workspace-write sandbox, and never-ask approval rather than disabling outer isolation; OpenCode returns unavailable until its installed help proves the required interface. A missing or incompatible required flag returns unavailable rather than silently choosing a weaker posture.
- Step 5: Implement custom adapter expansion
Substitute only whole-argument placeholders. Build stdin or positional prompts according to prompt_transport. Inherit only an allowlist of environment variables required for locale, PATH, HOME, temporary storage, and provider credential discovery by the CLI; never serialize values into receipts.
- Step 6: Run U2 green
Run: python3 -m unittest tests.test_agent_exec_config tests.test_agent_exec_adapters -v
Expected: PASS.
- Step 7: Host integration commit
Commit only U2 files with subject Add execution CLI adapter registry after host review and tests.
Task 3: U3 Execution Controller
Files:
- Create:
scripts/agent_exec.py - Create:
tests/test_agent_exec.py - Modify:
Makefile
Interfaces:
-
Consumes:
ExecutionConfig, adapterInvocation, a unit packet JSON document -
Produces: CLI commands
validateandrun; receipt schemaagent-exec-receipt/v1; transport refrefs/agent-exec/<run-id>/<attempt-id> -
Step 1: Write end-to-end controller tests red
Build temporary real Git repositories and fake executables. Cover:
def test_validate_does_not_contact_provider(): ...
def test_run_uses_fresh_process_and_worktree_per_attempt(): ...
def test_missing_profiles_fall_through_in_declared_order(): ...
def test_all_unavailable_authorizes_host_fallback_once(): ...
def test_failed_clean_can_continue_after_exact_restore(): ...
def test_failed_unknown_blocks_fallback(): ...
def test_scope_expansion_preserves_workspace_and_blocks(): ...
def test_worker_commit_or_head_move_is_rejected(): ...
def test_untracked_and_binary_files_enter_transport_inventory(): ...
def test_timeout_kills_process_group_and_records_receipt(): ...
def test_summary_and_raw_log_caps_are_enforced(): ...
def test_receipt_redacts_environment_secrets(): ...
def test_success_creates_base_parented_transport_without_touching_canonical_head(): ...
- Step 2: Run controller tests red
Run: python3 -m unittest tests.test_agent_exec -v
Expected: FAIL because scripts/agent_exec.py does not exist.
- Step 3: Implement unit packet validation
The JSON packet requires unit_id, goal, allowed_paths, verification, constraints, network, external_cost, and timeout_seconds. Reject absolute allowed paths, .., .git, secret files, an empty goal, or a timeout above project policy.
- Step 4: Implement private run state
Create /tmp/agent-exec-<uid>/<run-id>/ with mode 0700. Record config digest, base HEAD/tree, unit digest, attempts, worktree paths, process metadata, receipts, raw logs, transport refs, and cleanup state. Refuse symlinked or foreign-owned roots.
- Step 5: Implement candidate selection and fresh worktrees
For each role profile, create a new detached worktree from the fixed base, build the adapter invocation, launch a new process group, and never reuse a session. Missing/incompatible candidates become unavailable receipts without creating a provider request.
- Step 6: Implement supervision and bounded output
Capture stdout/stderr to private files, enforce idle/total timeout, terminate the process group, preserve exact exit state, and write bounded sanitized summaries. A process exit alone never proves no external side effect.
- Step 7: Implement scope and transport checks
Verify HEAD/base did not move, inventory tracked and untracked changes, reject paths outside allowed_paths, then create a host-owned synthetic transport commit with the recorded base as sole parent. Worker output never writes the canonical index or branch.
- Step 8: Implement state classification and fallback
Classify success, unavailable, failed-clean, failed-unknown, scope-expansion, invalid-config, and cancelled. Emit host-fallback-authorized only after every configured candidate is unavailable for the unit and every attempt has exact cleanup evidence.
- Step 9: Finalize CLI and Make target
Implement:
python3 scripts/agent_exec.py validate --repo <path>
python3 scripts/agent_exec.py run --repo <path> --role <role> --unit <packet> --receipt <path>
Return stable exit codes for success, host fallback, invalid configuration, blocked/unknown state, and ordinary unavailable exhaustion. Update make agent-exec-validate to call the final CLI.
- Step 10: Run U3 green
Run: python3 -m unittest tests.test_agent_exec_config tests.test_agent_exec_adapters tests.test_agent_exec -v
Expected: PASS.
- Step 11: Run governance regression
Run: make test-governance
Expected: PASS with no tracked worktree changes.
- Step 12: Host integration commit
Commit U3 files with subject Add recoverable execution agent controller after inspecting transport and authoritative test output.
Task 4: U4 Project Governance Integration
Files:
- Modify:
AGENTS.md - Modify:
policy/delivery.md - Modify:
policy/governance.md - Modify:
policy/adoption.md - Modify:
policy/tool-adaptation-matrix.md - Modify:
scripts/task.sh - Modify:
tests/test_task.py - Modify:
.task/feature-goal-first-delivery.md
Interfaces:
-
Consumes:
agent-exec validate/run, receipt schema,AGENT-EXECUTION.md -
Produces: agent-per-unit repository rule, task-card profile/attempt/fallback fields, and adoption/verification entrypoints
-
Step 1: Change task-card tests red
Update test_start_imports_precheck_and_creates_four_part_commit to require:
实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
配置摘要、角色、候选顺序、attempt、CLI/模型、终态、transport、fallback 与收据路径
Require the generated card to reject the obsolete fixed-Cursor wording.
- Step 2: Run the focused task test red
Run: python3 -m unittest tests.test_task.TaskLifecycleTests.test_start_imports_precheck_and_creates_four_part_commit -v
Expected: FAIL because the template still says cursor-per-unit.
- Step 3: Replace fixed-Cursor policy with the configuration pointer
Keep AGENTS.md short: require reading AGENT-EXECUTION.md when routing implementation, repair, test, or review work. Move adapter and fallback detail to the config/design and policy. Replace, rather than layer on, the current cursor-per-unit authority.
- Step 4: Update delivery and governance policy
State that external agents implement per bounded unit, host owns planning and delivery, and current-host implementation is permitted only with a valid host-fallback-authorized receipt. Preserve goal-first, evidence, Review, CI, merge, cleanup, and dual-key rules.
- Step 5: Update task generator and capability records
Generate the agent-per-unit fields and keep all initial attempt states NOT_RUN. Mark config parsing/controller tests as local enforced behavior, real provider/model identity as SELF unless independently attested, and remote platform admission as GATE.
- Step 6: Run U4 green
Run:
python3 -m unittest tests.test_task.TaskLifecycleTests.test_start_imports_precheck_and_creates_four_part_commit -v
python3 scripts/agent_exec.py validate --repo .
make test-governance
Expected: PASS.
- Step 7: Host integration commit
Commit only U4 paths with subject Route project work through configured agents.
Task 5: U5 Real CLI Probes and Combination Acceptance
Files:
- Create:
tests/fixtures/agent-exec/README.md - Modify:
tests/test_agent_exec.py - Modify:
.task/feature-goal-first-delivery.md
Interfaces:
-
Consumes: installed CLI profiles and
agent-exec run -
Produces: source-external probe receipts, final requirements mapping, and an adoption recommendation
-
Step 1: Add opt-in real-probe harness tests
Add a harness gated by RUN_REAL_AGENT_EXEC=1. It creates a temporary Git repository and a one-file unit packet. Each installed CLI is asked to create an exact marker file in its isolated workspace. The host verifies content, transport parent, changed paths, cleanup, model receipt status, and canonical-tree preservation. Missing OpenCode is expected unavailable.
- Step 2: Run fake and full local regression
Run:
python3 -m unittest tests.test_agent_exec_config tests.test_agent_exec_adapters tests.test_agent_exec -v
make test-governance
make test
make build
Expected: PASS; any pre-existing build warning remains non-blocking unless it affects the requested capability.
- Step 3: Run installed CLI probes one profile at a time
Use source-external receipts and a fixed temporary repository. Run Cursor first as the implementation acceptance path, then agy, Claude, PI, Codex, and Kimi. Do not run profiles concurrently against the same target. A provider quota or auth failure is recorded as unavailable for that probe and does not become a false PASS.
- Step 4: Exercise host fallback
Use a configuration whose candidates are deterministic missing executables. Verify host-fallback-authorized, then let the current host create the marker strictly inside the receipt's allowed scope. Run the authoritative verification and record the fallback attempt separately from external-agent success.
- Step 5: Verify R1-R10 and migration
Map every spec requirement to config/parser/adapter/controller/governance tests and exact receipts. Confirm no active fixed-Cursor authority remains, no task outside this scope was modified, and no CLI received commit/push/PR/merge/deploy authority.
- Step 6: Commit acceptance records
Commit only tracked fixtures and task-card management metadata with subject Record execution agent acceptance evidence. Raw CLI logs and source-external receipts remain outside Git.
- Step 7: Enter normal delivery gates
Run make task-sync before formal acceptance, Review request, and merge. Obtain an independent code-review receipt, push one PR, wait for exact-HEAD test, test-pg, and svc-realcombo, merge through Gitea with force_merge=false, verify the actual main tree, and safely close the task.
Requirements Trace
| Requirement | Owning task and proof |
|---|---|
| R1 project-level single configuration | Task 1 parser/schema tests; Task 4 AGENTS.md pointer and task-card contract |
| R2 declarative role routing | Task 1 role-order tests; Task 3 ordered selection tests |
| R3 optional model | Task 1 auto -> None; Task 2 auto/fixed-model argv tests |
| R4 run-everything priority | Task 2 permission mapping tests; Task 5 installed-CLI probes |
| R5 multi-CLI adapters | Task 2 fake contracts for seven adapters; Task 5 real installed-CLI outcomes |
| R6 safe host fallback | Task 3 exhaustion, restoration, unknown-state and fallback tests; Task 5 real fallback drill |
| R7 low host-model consumption | Task 3 output-cap tests; Task 5 receipt and raw-log boundary review |
| R8 strict scope | Task 3 allowed-path, HEAD movement, transport, and retained-workspace tests |
| R9 controlled custom adapter | Task 1 placeholder/shell rejection; Task 2 direct-argv custom adapter tests |
| R10 recoverable evidence | Task 3 attempt/transport/cleanup receipts; Task 4 task-card fields; Task 5 evidence mapping |
Definition of Done
- R1-R10 have code, tests, and exact evidence mappings.
AGENT-EXECUTION.mdvalidates and is the only project execution-routing authority.- Cursor implements every repository-writing task in this plan unless a recorded all-unavailable receipt authorizes host fallback.
- Every successful worker result is scope-inspected, authoritatively tested, and committed by the host.
- Fake adapters cover all built-in routes; installed real CLIs have source-external probe outcomes.
- Full local test/build pass on the fixed candidate, independent Review has no blocking findings, and exact-HEAD CI passes before merge.
- No deployment, platform-protection change, secret persistence, or unrelated cleanup occurs.