# Feature Spec: MCP‑First, Platform‑Agnostic Multi‑Agent Orchestrator for Parallel Feature Development (AOP)

> **Purpose of this document**: A single, implementable specification an AI agent can use to build the complete feature set described across the conversation: an **MCP-first “kernel”** plus a **Supervisor Runtime** that runs an **Orchestrator/Agent‑Cluster architecture** to develop **multiple features in parallel** with deterministic gates, collision detection, locks, worktrees, evidence, and auditable state.

---

## 1. Objectives

### 1.1 Must‑Have Capabilities

- **MCP-first kernel** (deterministic): Git worktrees, patch application, gate execution, locking, collision detection, evidence/log capture, and canonical state writing.
- **Platform-agnostic**: Gate commands are defined by repo-local config (`agentic/orchestrator/gates.yaml`) and executed by MCP; no toolchain-specific logic in MCP beyond generic process execution and optional artifact parsing.
- **Implementation substrate**: The orchestrator implementation itself MUST be delivered as an **Nx monorepo**.
- **Testing standard**: The orchestrator implementation test framework MUST be **Vitest**.
- **Scope of Nx+Vitest requirement**: This applies only to the orchestrator control-plane codebase (CLI/Supervisor/MCP). It does not constrain managed target repository toolchains.
- **Orchestrator/Agent Cluster model**:
  - User only interacts with **Orchestrator Agent**.
  - Orchestrator consumes `spec.md` per feature and supervises an **Agent Cluster (Planner, Builder, QA)** per feature.
  - **Supervisor Runtime** spawns worker sessions and enforces role permissions.
- **Parallelism**: Support at least **5 features** in parallel with isolation using **git worktrees** and conflict prevention using **locks + collision detection** at plan time.
- **Host review**: All changes must be visible on host filesystem prior to merge. User review occurs while feature diffs are still unmerged.
- **Explicit merge control**: MCP/Supervisor MUST NOT auto-merge as part of gate success alone. Commit + merge is only allowed through an explicit `feature.ready_to_merge` tool call after user approval.

### 1.2 Non‑Goals

- Not building a proprietary agent model framework.
- Not requiring cloud infrastructure.
- Not forcing auto-merge; explicit user review and approval remain default.
- Not implementing a full PR hosting integration (optional later).

---

## 2. Concepts and Terminology

- **Control Plane (deterministic)**: MCP server + tool handlers.
- **Cognitive Plane (non-deterministic)**: Orchestrator agent and worker agents generating plans/patches and reacting to logs.
- **Agent Cluster (AC)**: Planner + Builder + QA agents for a single feature.
- **Ralph Loop**: propose → validate (via MCP gates) → repair → repeat.
- **Worktree**: Separate checkout per feature branch via `git worktree`.
- **Gate**: A deterministic command sequence that must pass (exit code 0 and policy constraints) to advance.
- **Lock**: Exclusive resource ownership (openapi, db migrations, protected libs, etc.).

---

## 3. System Architecture

### 3.1 Processes

**A) MCP Server (inside Docker, bind-mounted repo)**

- Exposes tools via MCP.
- Runs git and gate commands inside bind mount.
- Enforces schemas, policies, locks, collisions, and patch constraints.
- Writes canonical state and evidence.

**B) Supervisor Runtime (outside MCP, vendor-agnostic)**

- Responsible for “spinning up agents” (Orchestrator + workers).
- Applies role-scoped tool permissions.
- Runs orchestration algorithm across features and clusters.

**C) Agent Runtime(s) (Claude Code, Codex CLI, etc.)**

- Connect to MCP tools.
- Generate structured artifacts and diffs under Supervisor control.

> **Key design decision**: MCP server is vendor-agnostic and should **not** call Claude/Codex directly. The Supervisor Runtime owns provider-specific session management.

### 3.2 Docker + Host Visibility

- Target repo **MUST be bind-mounted** into container at `/repo`.
- Worktrees MUST be created under `/repo/.worktrees/<featureId>` (or configurable but within bind mount).
- Therefore host sees changes immediately at `<repo>/.worktrees/<featureId>`.

### 3.3 Isolation and Parallelism

- For N features in parallel (target 5): one worktree per feature branch.
- MCP never switches the main working tree branch; it operates only in the worktree per feature.
- Concurrency controls:
  - gate execution concurrency limit (CPU)
  - lock ownership checks
  - collision rejection/blocking at plan submit

### 3.4 State Consistency (Atomicity + Concurrency)

- All writes to `agentic/features/index.json` and `agentic/features/*/state.md` MUST be atomic using write-temp + fsync + rename semantics.
- MCP MUST serialize mutations with file locks:
  - global index lock for `index.json` updates
  - per-feature lock for `state.md` + `plan.json` updates
- State-bearing files MUST include `version` (monotonic integer).
- Mutating tools MUST use optimistic concurrency (`expected_version`) and fail with `error=version_conflict` when stale.
- Deterministic ordering rule for concurrent lock acquisitions: lexicographic lock ordering by resource id.

---

## 4. Repo File Layout (Canonical)

At repo root:

```
agentic/
  orchestrator/
    gates.yaml
    policy.yaml
    agents.yaml
    prompts/
      planner.system.md
      builder.system.md
      qa.system.md
    schemas/
      plan.schema.json
      state.schema.json
      index.schema.json
      gates.schema.json
      policy.schema.json
      agents.schema.json
      qa_test_index.schema.json
    tools/
      catalog.json
      protocol.json
      errors.schema.json
      schemas/
        input/*.schema.json
        output/*.schema.json
    tools.md
  features/
    index.json
    my_feature/
      spec.md
      state.md
      plan.json
      qa_test_index.json
      decisions.md
      logs/
      evidence/
    another_feature/...
.git/
.worktrees/
  my_feature/
  another_feature/
  ...
```

---

## 5. Deterministic vs Agent Responsibilities

### 5.1 MCP (deterministic) MUST do

- Validate `plan.json` against schema and policy.
- Validate `state.md` front matter against schema when writing.
- Detect collisions at plan time using accepted plans.
- Enforce locks for contract resources and protected areas.
- Apply patches with policy enforcement (allowed areas, planned files, lock constraints).
- Run gates from config; capture logs; parse artifacts; enforce thresholds.
- Persist evidence and update state files.
- Produce review bundles (diff summaries, evidence summaries).
- Enforce legal status transitions and optimistic concurrency on state changes.
- Enforce configuration precedence across policy/gates/plan overrides.
- Maintain per-feature `qa_test_index.json` from current worktree diff (file + line hunk granularity).
- Provide resumable context bundles for agent respawn (state, plan, diff/evidence summary, QA index).

### 5.2 Agents MUST do

- Interpret `spec.md` and existing code patterns.
- Produce `plan.json` and diffs (unified patches).
- Use Ralph Loop by reacting to MCP gate outputs.
- Orchestrator chooses collision resolution strategy and merge order.

### 5.3 Supervisor Runtime MUST do (enforcement that is _not_ MCP)

- Spawn/maintain sessions for orchestrator + workers.
- Enforce role tool permissions (tool-call firewall).
- Route tool results and failure logs to the correct worker.
- Decide when to advance phase: planning → building → qa → ready.
- Support ephemeral QA workers: close QA session after a test batch and respawn later with MCP-sourced context.

---

## 6. Schemas (Formal Definitions)

> **Requirement**: MCP server MUST load these schemas from `agentic/orchestrator/schemas/` and validate on relevant tool calls. Rejection MUST be structured and actionable.

### 6.0 MCP Tool Contract Schemas

In addition to runtime state schemas, the MCP boundary MUST be formalized with:

- `agentic/orchestrator/tools/catalog.json` as the single source of truth for tool metadata and schema references.
- `agentic/orchestrator/tools/protocol.json` for pinned MCP protocol/SDK/transports.
- `agentic/orchestrator/tools/errors.schema.json` for normalized error envelopes.
- `agentic/orchestrator/tools/schemas/input/*.schema.json` and `agentic/orchestrator/tools/schemas/output/*.schema.json` for per-tool contracts.

Contract validation MUST fail CI when protocol pins drift or when `catalog.json`, `tools.md`, and schema files are out of sync.

### 6.1 `plan.json` JSON Schema (`plan.schema.json`)

**Required fields**:

- `feature_id` (string, derived from spec filename; pattern `^[a-z0-9_][a-z0-9_-]*$` recommended)
- `plan_version` (integer, starts at `1`, increments by `+1` on each accepted update)
- `summary` (string, min 5 chars)
- `allowed_areas` (array of non-empty strings)
- `forbidden_areas` (array; can be empty)
- `base_ref` (string, commit SHA or ref used for planning snapshot)
- `files` object:
  - `create` array
  - `modify` array
  - `delete` array
- `contracts` object with enums:
  - `openapi`: `"none" | "modify"`
  - `events`: `"none" | "modify"`
  - `db`: `"none" | "migration"`
- `acceptance_criteria` (array of non-empty strings, minItems=1)
- `gate_profile` (string, default `"default"`)
- Optional: `gate_targets` (array of strings)
- Optional: `verification_overrides` (object) – see below
- Optional: `risk` (array of strings)
- Optional: `revision_of` (integer plan version being replaced)
- Optional: `revision_reason` (string)

**Verification overrides** (optional):

- `modes.fast.steps` array of gate step overrides (rare; discourage)
- `modes.full.steps` overrides
  Default behavior is use `gates.yaml`.

**Plan revision rules**:

- First accepted plan MUST have `plan_version=1` and no `revision_of`.
- `plan.update` MUST require `expected_plan_version` and reject stale updates.
- MCP MUST re-run schema, policy, lock, and collision checks for every revision.

**Plan Schema (canonical draft)**: see Appendix A for full JSON Schema.

### 6.2 `state.md` JSON Schema (`state.schema.json`)

State file is Markdown with YAML front matter. MCP validates the front matter object.

Required fields:

- `feature_id` (string)
- `version` (integer, monotonic)
- `branch` (string)
- `worktree_path` (string)
- `status` enum: `planning|building|qa|blocked|ready_to_merge|merged|failed`
- `gate_profile` (string)
- `gates` object with gate result enums `pass|fail|na`
- `locks.held` array
- `collisions` structure
- `cluster` session ids (strings; may be `"unknown"` if runtime doesn’t provide)
- `role_status` for planner/builder/qa
- `last_updated` ISO string
- Optional: `evidence` object (last gate id, artifacts)
- Optional: `status_reason` (string)

### 6.3 `index.json` JSON Schema (`index.schema.json`)

Tracks global orchestration status.

Required fields:

- `version` integer (monotonic)
- `active` array of feature ids
- `blocked` array
- `merged` array
- `locks` object mapping resource name → feature id or null
- `lock_leases` object mapping resource name → lease metadata (holder, lease_id, expires_at)
- `blocked_queue` array (for `collision_policy=block`)
- Optional: `updated_at`

### 6.4 `gates.yaml` Schema (`gates.schema.json`)

YAML is validated by loading and validating against JSON schema equivalent.

Required fields:

- `version` number
- `profiles` object:
  - profile name → profile object
- Profile object required:
  - `modes` object:
    - mode name (`fast`, `full`, optional `merge`) → array of steps
- Step object required:
  - `name` string
  - `cmd` array of strings (exec args)
  - Optional: `cwd` string (relative to worktree root)
  - Optional: `env` object
  - Optional: `timeout_seconds` number

Optional:

- `parsers.coverage` with:
  - `type`: `none|lcov|junit_xml|jacoco_xml|cobertura_xml|custom`
  - `path` string
- `thresholds.coverage_line_min` number 0..1
- `thresholds.coverage_branch_min` number 0..1
- `thresholds.coverage_line_target` number 0..1
- `thresholds.coverage_branch_target` number 0..1
- `capabilities` list (declares supported parsers)

### 6.5 `policy.yaml` Schema (`policy.schema.json`)

Required fields:

- `version`
- `commit_policy.allow_commit` boolean
- `commit_policy.allow_merge` boolean
- `patch_policy.enforce_plan` boolean
- `patch_policy.enforce_allowed_areas` boolean
- `locks.resources` array
- `locks.contract_to_resource` object (`openapi|events|db` to lock resource id)
- `locks.lease_ttl_seconds` number
- `protected_areas` array
- `exclusive_areas` array (optional but recommended for high-collision repos)
- `required_modes` array
- `required_merge_mode` string
- `collision_policy` enum `reject|block`
- `config_precedence` object defining deterministic precedence:
  - `policy_hard_constraints`
  - `gates_profile_defaults`
  - `plan_verification_overrides`
- `locks.acquire_behavior` enum `wait|fail` (default `wait`)
- `locks.default_wait_timeout_seconds` number (default `300`)
- `locks.acquire_backoff` object with jittered exponential settings
- `rbac` matrix: role -> allowed tool names
- `path_rules` object:
  - `matching` enum `repo_prefix|glob`
  - `normalize_paths` boolean (must be true)
  - `allow_symlink_traversal` boolean (must default false)
- `execution.default_step_timeout_seconds` number (default `600`)
- `execution.retry_policy` object:
  - `transient_max_retries` number (default `1`)
  - `transient_error_codes` array
  - `non_retryable_error_codes` array
- `execution.env_allowlist` array
- `implementation.workspace` enum `nx` (required for this implementation)
- `testing.framework` enum `vitest`
- `testing.coverage.minimums.line` number (default `0.90`)
- `testing.coverage.minimums.branch` number (default `0.90`)
- `testing.coverage.targets.line` number (default `1.00`)
- `testing.coverage.targets.branch` number (default `1.00`)
- `merge_policy.require_user_approval` boolean
- `merge_policy.allowed_strategies` array (`merge_commit|squash|rebase`)
- `worktree.base_branch` string (for initial branch cut)
- Optional additional thresholds (coverage, performance, repo-specific gates) beyond the required testing minima above.

Policy interpretation notes:

- `commit_policy.*` controls direct low-level commit tools (`repo.commit`).
- `merge_policy.*` controls high-level merge promotion (`feature.ready_to_merge`), including the commit created as part of merge promotion.
- `implementation.workspace` and `testing.*` constrain the orchestrator codebase implementation; they do not remove platform-agnostic support for managed target repositories.

### 6.6 Path Canonicalization and Matching Rules

- MCP MUST canonicalize all candidate paths to repo-relative POSIX form before validation.
- Any path that escapes repo root (`..`, absolute host paths) MUST fail with `error=path_out_of_bounds`.
- Symlink traversal outside repo root is forbidden by default and controlled by `policy.path_rules.allow_symlink_traversal`.
- Area matching semantics are policy-controlled:
  - `repo_prefix`: area `src/api` means any path with that normalized prefix.
  - `glob`: area values interpreted as POSIX globs.
- `repo.apply_patch` MUST evaluate renamed, copied, deleted, and mode-changed files against plan/policy with both old and new paths.

### 6.7 `agents.yaml` Schema (`agents.schema.json`)

Repo-level worker prompt configuration.

Required fields:

- `version`
- `roles` object:
  - `planner.system_prompt_path` (optional)
  - `builder.system_prompt_path` (optional)
  - `qa.system_prompt_path` (optional)
- `missing_prompt_behavior` enum `ignore|error` (default `ignore`)
- Optional `runtime` object:
  - `default_provider` enum `codex|claude|gemini|custom`
  - `default_model` string
  - `provider_config_env` string (env var name containing provider auth/config)
  - `role_provider_overrides` object (optional per-role provider/model overrides)

Prompt loading rules:

- If configured prompt path exists, Supervisor MUST load and inject it as the role system prompt.
- If path is absent or file missing and `missing_prompt_behavior=ignore`, Supervisor uses built-in defaults.
- Prompt paths MUST be repo-relative and validated with path rules (§6.6).
- Provider defaults in `runtime` are used only when CLI/env do not override.

### 6.8 `qa_test_index.json` Schema (`qa_test_index.schema.json`)

Per-feature QA execution index used to keep QA agent context small and resumable.

Required fields:

- `feature_id`
- `version` (integer, monotonic)
- `source_diff_ref` (string identifying diff snapshot)
- `items` array where each item includes:
  - `path` (repo-relative)
  - `hunks` array of `{start_line, end_line, change_type}`
  - `required_tests` array of strings
  - `status` enum `pending|running|passed|failed|waived`
  - optional `last_run_at`
  - optional `evidence_refs` array

Rules:

- MCP MUST update file/hunk entries after every successful `repo.apply_patch`.
- MCP MUST compute `required_tests` deterministically from configured gate steps and changed paths/hunks so QA sees exact pending test obligations at any time.
- QA agent MUST update statuses/evidence after each executed test batch.

---

## 7. MCP Tool Catalog (Deterministic Kernel)

### 7.1 Tool Contract Norms

All tools MUST:

- Accept `actor_type` (`orchestrator|planner|builder|qa|system`) and `actor_id` (string).
- Return JSON with at least:
  - `ok` boolean
  - `data` object if ok
  - `error` object if not ok
  - `evidence` object when relevant (log paths, command, exit code)

Tools MUST be idempotent when reasonable (`ensure_worktree`, `feature.init`).
Mutating tools MUST require `expected_version` for optimistic concurrency where a state/index file is updated.
Authorization MUST be deny-by-default; any disallowed role/tool attempt fails with `error=forbidden_tool_for_role`.

### 7.1.1 Normative RBAC Matrix (Default)

- `orchestrator`: may call all read/report tools, `feature.init`, `plan.get`, `plan.submit`, `plan.update`, `locks.acquire`, `locks.release`, `gates.run`, `feature.ready_to_merge`.
- `planner`: may call read tools, `plan.submit`, `plan.update`, `collisions.scan`.
- `builder`: may call read tools, `repo.apply_patch`, `repo.status`, `repo.diff`, `gates.run`.
- `qa`: may call read tools, `repo.apply_patch`, `repo.diff`, `gates.run`, `evidence.latest`, `qa.test_index_get`, `qa.test_index_update`.
- `system`: unrestricted internal automation role.
- `feature.state_patch` is restricted to `system|orchestrator` unless policy explicitly expands access.
- `locks.acquire`/`locks.release` are restricted to `orchestrator|system`; worker roles request lock actions via `REQUEST`.

### 7.2 Required Tools (V1)

#### Feature lifecycle

1. `feature.discover_specs()`

- Returns list of `{feature_id, spec_path}` under `agentic/features/*/spec.md`.

2. `feature.init(feature_id)`

- Creates feature folder structure if missing.
- Creates/ensures branch + worktree (see `repo.ensure_worktree`).
- Writes `state.md` default with `status=planning`.

3. `feature.get_context(feature_id)`

- Returns: spec.md text, parsed state front matter, plan.json (if exists), latest evidence summary, and current `qa_test_index.json`.

4. `feature.state_get(feature_id)`

- Returns parsed state front matter + raw markdown body.

5. `feature.state_patch(feature_id, expected_version, patch)`

- Restricted: allowed only for `actor_type=system|orchestrator` by policy; typically MCP internal.
- Validates against schema before writing.

6. `feature.log_append(feature_id, note)`

- Appends to `decisions.md` or `state.md` body section; includes `actor` stamp.

#### Plan

7. `plan.submit(feature_id, plan_json, expected_version?)`

- Validates plan schema.
- Enforces `policy.yaml` constraints:
  - required fields
  - disallowed contract changes without locks
  - forbidden/protected areas
- Collision scan against other accepted plans (see §8).
- Persists `plan.json`.
- Updates `state.md` gates: `plan=pass` and status to `building` if allowed.

8. `plan.get(feature_id)`

9. `plan.update(feature_id, expected_plan_version, plan_json)`

- Requires `plan_json.plan_version = expected_plan_version + 1`.
- Requires `plan_json.revision_of = expected_plan_version`.
- Re-runs full validation, lock checks, and collision scan.
- Overwrites `plan.json` atomically only if checks pass.
- Existing worktree diffs are retained, but all subsequent `repo.apply_patch` operations MUST validate against the latest accepted plan.

#### Repo/worktree

10. `repo.ensure_worktree(feature_id)`

- Ensures `.worktrees/<feature_id>` exists and is checked out to `<feature_id>` branch.
- If branch is newly created, cut it from `policy.worktree.base_branch` at the current head SHA.
- Must store branch name in state.

11. `repo.apply_patch(feature_id, unified_diff)`

- Validates: plan exists (unless policy allows).
- Parses patch to determine touched paths.
- Enforces:
  - touched paths ⊆ allowed_areas
  - no forbidden/protected area edits without permission/lock
  - touched files are in plan’s create/modify/delete lists (strict by default)
  - contract resources require locks
  - path normalization and path-out-of-bounds checks (§6.6)
- Applies patch in the worktree.
- Returns updated `git status --porcelain` plus list of changed files.
- Rebuilds or incrementally updates `qa_test_index.json` for changed files/hunks.

12. `repo.status(feature_id)`
13. `repo.diff(feature_id, options?)`

- Options: `--stat`, `--name-only`, etc.

14. `repo.read_file(feature_id, path)`
15. `repo.search(feature_id, query)`

- Wrapper around `rg` with safe defaults; returns file/line matches.

16. `repo.diff_bundle(feature_id)`

- Returns a “review bundle”:
  - diff stat
  - full diff (or path to stored diff)
  - touched file list
  - last gate summary

17. `feature.ready_to_merge(feature_id, commit_message, merge_strategy, user_approval_token)`

- Preconditions:
  - feature status is `ready_to_merge`
  - required `full` + `merge` modes pass (policy-controlled)
  - `merge_policy.require_user_approval=true` implies valid `user_approval_token`
- Performs deterministic commit in feature branch and merge into base branch.
- On success:
  - updates state status to `merged`
  - appends merge evidence (commit SHA, merge SHA, strategy, gate snapshot)
  - releases held locks.

> Low-level git tools MAY exist for debugging but SHOULD be blocked by RBAC for non-system roles: 18) `repo.commit(feature_id, message)` (internal/optional) 19) `repo.rebase_onto_main(feature_id)` (internal/optional) 20) `repo.merge(feature_id)` (internal/optional)

#### Gates/evidence

21. `gates.list(profile?)`
22. `gates.run(feature_id, profile, mode)`

- Loads `gates.yaml`, selects profile and mode.
- Runs steps sequentially (or optionally parallel if configured, but default sequential for determinism).
- Captures stdout/stderr per step to `logs/`.
- On non-zero exit code: stop and report failure.
- If configured, parse coverage artifact and enforce thresholds.
- Writes state updates:
  - per-step results
  - overall mode result
  - evidence pointers

23. `evidence.latest(feature_id)`

- Returns last gate run summary with key log excerpt tail.

#### QA context / test index

24. `qa.test_index_get(feature_id)`

- Returns parsed `qa_test_index.json`, summary counts by status, and exact pending `required_tests` grouped by file/hunk.

25. `qa.test_index_update(feature_id, expected_version, updates, evidence_refs?)`

- Allowed for `qa|system|orchestrator`.
- Applies status updates to index entries and increments index version.
- Requires line-hunk references when marking entries `passed|failed|waived`.
- MUST be called by QA agent after each test batch.

#### Locks/collisions

26. `locks.acquire(resource, feature_id, wait_timeout_seconds?)`

- Resource names from policy.
- Stores in `index.json` and feature state.
- Enforces ownership.
- MUST create lease metadata (`lease_id`, `expires_at`) and renew while holder is healthy.

27. `locks.release(resource, feature_id)`

28. `collisions.scan()`

- Returns global collision matrix based on accepted plans.

#### Reporting (for orchestrator UX)

29. `report.dashboard()`

- Returns summary of all features: status, blocks, locks, last gates.

30. `report.feature_summary(feature_id)`

- Returns state summary + diff summary + latest evidence links.

---

## 8. Collision Detection and Resolution

### 8.1 Collision Types (detected by MCP at `plan.submit`)

- **File collision:** two accepted plans modify the same file path.
- **Area collision:** overlap within protected_areas or explicitly configured “exclusive areas”.
- **Contract collision:** multiple plans request `openapi/events/db` modification.
- **Migration collision:** multiple plans set `db=migration` simultaneously (serializable via lock).

### 8.2 Collision Policy

Defined in `policy.yaml`:

- `collision_policy: reject|block`
  Default: `reject` with structured report so orchestrator can revise.

If `collision_policy=block`:

- MCP MUST persist blocked plan requests in `index.json.blocked_queue`.
- Queue entries MUST include `{feature_id, plan_version, detected_at, collision_fingerprint, required_resources}`.
- MCP MUST re-evaluate queued entries when relevant locks are released or plans change.
- Queue order MUST be deterministic: oldest `detected_at`, then `feature_id`.

### 8.3 Collision Report Format

On collision, MCP returns:

- collision items
- owning feature ids
- normalized path/resource identifiers
- collision fingerprint (stable hash for dedupe)
- recommended actions:
  - acquire lock
  - revise plan to avoid overlap
  - create “shared prerequisite” feature

Orchestrator agent decides strategy; Supervisor updates states accordingly.

---

## 9. Locks

### 9.1 Lock Resources

Configured by `policy.yaml`, recommended defaults:

- `openapi`
- `events`
- `db_migrations`
- optionally: `protected:libs/core`

Contract-to-lock mapping MUST be explicit:

- `contracts.openapi=modify` requires lock `policy.locks.contract_to_resource.openapi` (default `openapi`).
- `contracts.events=modify` requires lock `policy.locks.contract_to_resource.events` (default `events`).
- `contracts.db=migration` requires lock `policy.locks.contract_to_resource.db` (default `db_migrations`).

Lease rules:

- Locks are leased, not perpetual.
- Lease TTL is `policy.locks.lease_ttl_seconds`.
- Supervisor/MCP heartbeat renews leases.
- Expired leases may be reclaimed by MCP with `error=stale_lock_reclaimed` evidence.
- Default acquisition behavior is queued/blocking (`locks.acquire_behavior=wait`) with timeout/backoff from policy.

### 9.2 Lock Enforcement

- `plan.submit` fails if plan indicates contract changes but lock not acquired.
- `repo.apply_patch` fails if patch touches contract paths without lock.
- `feature.ready_to_merge` MUST fail if any required lock is lost before merge.
- Lock operations are performed by `orchestrator|system`; workers issue `REQUEST` outputs for arbitration.

---

## 10. Supervisor Runtime (Cluster Runner) — Required Behavior

### 10.1 Responsibilities

- Maintain **one Orchestrator Agent** session (user-facing).
- Spawn **three worker sessions per feature**:
  - Planner
  - Builder
  - QA
- Enforce **role-scoped tool access**:
  - Implement a “tool-call firewall” that blocks calls not allowed for the role.
- Load role system prompts from `agentic/orchestrator/agents.yaml` when configured and inject at worker creation.
- Resolve agent runtime provider/model selection from CLI/env/config and initialize the corresponding provider adapter.
- Maintain lock lease heartbeats for active features.
- Support restart recovery:
  - reattach to existing sessions when possible
  - reconstruct state from `index.json` + feature states when sessions are unavailable
  - re-drive blocked/retryable operations deterministically
- Run orchestration loop across features:
  1. init
  2. planning wave
  3. collision arbitration
  4. build wave
  5. qa wave
  6. readiness report (full gates)

### 10.2 Vendor-Agnostic Worker Interface

Supervisor must treat workers abstractly:

- Worker input: `{role, feature_id, context_bundle, instructions, last_tool_results}`
- Worker input MUST include runtime selection metadata: `{provider, model, provider_config_ref}`.
- Worker output types:
  - `PLAN_SUBMISSION` (planner emits plan json)
  - `PATCH` (builder/qa emits unified diff)
  - `NOTE` (decisions/summary)
  - `REQUEST` (ask for lock, ask to amend plan, ask for more context)

Supervisor routes outputs to MCP tools and returns results to worker.

Context bundle MUST include:

- current feature state + accepted plan
- latest evidence summary
- current diff summary
- `qa_test_index.json` for QA workers

### 10.3 Concurrency

Configurable:

- `max_active_features` default 5
- `max_parallel_gate_runs` default 2–3 (CPU dependent)
- `max_iterations_per_phase` default 5–10
- `lock_acquire_backoff_seconds` default jittered exponential backoff
- `lock_wait_timeout_seconds` default 300

### 10.4 Phase Advancement Rules

- Feature can move to `building` only after `plan.submit` accepted.
- Feature can move to `qa` only after `fast` gates pass (policy-configurable).
- Feature can move to `ready_to_merge` only after `full` gates pass; this state is still unmerged and reviewable.
- User review MUST happen while in `ready_to_merge` before merge execution.
- `feature.ready_to_merge` (explicit call) performs commit + merge and transitions to `merged` on success.
- Feature becomes `blocked` on lock denial or collision policy outcome.

Supervisor updates status through MCP state patch tool (or via MCP internal updates triggered by other tools).
Supervisor proposes transitions; MCP is source of truth and MUST validate transition legality.

### 10.5 Legal Status Transition Matrix (Normative)

- `planning -> building`: only via accepted `plan.submit` or `plan.update`.
- `building -> qa`: only via passing required `fast` gates.
- `qa -> ready_to_merge`: only via passing required `full` gates.
- `ready_to_merge -> merged`: only via successful `feature.ready_to_merge` call.
- `* -> blocked`: on lock/collision denial, unrecoverable gate failure, or policy violation.
- `blocked -> planning|building|qa`: only after explicit unblock event with `status_reason` update.
- Any undefined transition MUST fail with `error=invalid_status_transition`.

### 10.6 Crash Recovery and Resume Algorithm

On Supervisor or MCP restart:

1. Load and validate `index.json` and all active feature `state.md` files.
2. Reconcile lock leases:
   - if holder heartbeat is fresh, preserve lease
   - if stale, reclaim and mark affected features `blocked` with reason
3. Reconstruct pending work from:
   - features not terminal (`merged|failed`)
   - queued collisions (`blocked_queue`)
   - gate runs with no terminal evidence record
   - current `qa_test_index.json` entries not in terminal status (`passed|waived`)
4. Resume orchestration from the earliest incomplete phase per feature.
5. All tool retries MUST be idempotent and keyed by operation id; duplicate execution must not double-apply patches or merges.

### 10.7 Ephemeral QA Session Lifecycle (Context Control)

- QA workers are intentionally short-lived to prevent unbounded context growth.
- After each QA test batch:
  - QA worker MUST call `qa.test_index_update`.
  - Supervisor MUST close the QA worker session.
- On next QA iteration, Supervisor MUST be able to spawn a fresh QA worker that consumes:
  - `feature.get_context`
  - `qa.test_index_get`
  - latest gate/evidence summaries
- This resume flow MUST preserve progress without requiring prior conversational context.

---

## 11. Orchestrator Agent Contract (User-Facing Behavior)

Orchestrator Agent must:

- Discover provided specs from CLI-resolved inputs (`-fi`/`-fl`) or canonical repo discovery and ask user which to run if more than N are present (optional; default pick first N).
- Report dashboard status periodically (using `report.dashboard`).
- Decide collision resolution (revise plan, wait, shared prerequisite).
- Provide user review pack before merge execution:
  - `repo.diff_bundle`
  - `report.feature_summary`
  - evidence links
- Request explicit user approval token before invoking `feature.ready_to_merge`.

Orchestrator must never claim gates passed without MCP results.

---

## 12. Gate Execution (Platform-Agnostic)

### 12.1 Command Execution

MCP executes `cmd` arrays from `gates.yaml` with:

- `cwd` set to the feature worktree root
- safe environment with secret scrubbing enabled by default
- environment variable pass-through restricted to `policy.execution.env_allowlist`

### 12.2 Parsers / Metrics

Supported parsers (V1):

- `none`
- `lcov` (line coverage)
- `junit_xml` (pass/fail + counts)
- `jacoco_xml` (line coverage)
- `cobertura_xml` (line coverage)

If parser type is unrecognized: gate fails with `error=unsupported_parser` unless policy allows skip.

### 12.3 Threshold Enforcement

If coverage threshold configured in policy or gates:

- MCP must parse coverage and compare.
- Failure sets `gates.coverage_* = fail`.

For this implementation:

- Minimum blocking thresholds are `line >= 0.90` and `branch >= 0.90`.
- Target thresholds are `line = 1.00` and `branch = 1.00` (reported as target attainment metrics).

### 12.4 Timeouts and Retry Defaults (Normative)

- If a gate step omits `timeout_seconds`, MCP MUST apply `policy.execution.default_step_timeout_seconds` (default `600`).
- Retries are allowed only for transient execution failures per `policy.execution.retry_policy.transient_error_codes`.
- Default transient retry count is `1`.
- Schema/policy/authorization/path violations are non-retryable by default.

### 12.5 Configuration Precedence (Normative)

For any gate execution parameter, precedence is:

1. `policy.yaml` hard constraints (cannot be relaxed)
2. `gates.yaml` selected profile/mode defaults
3. `plan.json.verification_overrides` (may only narrow scope or tighten thresholds)

If a lower-priority source attempts to relax a higher-priority constraint, MCP MUST fail with `error=invalid_override_precedence`.

### 12.6 Nx + Vitest Testing Contract (Implementation)

- The orchestrator codebase MUST be implemented as an Nx monorepo.
- The unit/integration test runner for the orchestrator codebase MUST be Vitest.
- Default test gate commands for orchestrator projects SHOULD use Nx task execution (for example `nx test <project> --coverage`).
- Coverage artifacts MUST be emitted in a parser-compatible format (`lcov` preferred) so MCP can enforce thresholds.

---

## 13. Docker Deployment Requirements

### 13.1 Required Mounts

- Repo mounted to `/repo`
- Optional caches:
  - `/cache/pnpm`
  - `/cache/m2`
  - `/cache/cargo`

### 13.2 Toolchains

Repo toolchains must exist in the container image used to run MCP (either per-repo image or universal). MCP itself stays toolchain-agnostic.
For this implementation, container/runtime environment MUST include Node.js and Nx-compatible tooling required to run Vitest via Nx tasks.

---

## 14. Acceptance Criteria (Complete)

A) **Kernel correctness**

- Plan schema validation rejects invalid plans with actionable errors.
- Plan revisions are versioned and protected by optimistic concurrency.
- Patch enforcement prevents edits outside plan/allowed areas.
- Gate runner executes config commands and stores logs/evidence.
- Locks and collisions prevent conflicting edits deterministically.
- State files are updated only by MCP and validated by schema.
- State/index writes are atomic and race-safe under parallel runs.

B) **Parallelism and visibility**

- 5 features can run concurrently with 5 worktrees.
- User can open `.worktrees/<feature_id>` (for example `.worktrees/my_feature`) locally to review unmerged changes.

C) **Platform-agnostic gates**

- Same MCP works on managed target repositories:
  - Nx repo (nx commands in gates.yaml)
  - Java repo (mvn/gradle commands in gates.yaml)
  - Rust repo (cargo commands in gates.yaml)
    No MCP code changes required.

D) **Orchestrator/cluster architecture**

- User interacts only with Orchestrator Agent.
- Supervisor spawns worker sessions per feature.
- Tool-call firewall enforces role tool permissions.
- System produces dashboard and per-feature review bundle.
- Merge only occurs via explicit `feature.ready_to_merge` call after user approval.
- If role prompts exist in repo config, spawned workers receive them as system prompts.
- QA worker runs can be closed/restarted while preserving progress via MCP-backed `qa_test_index.json`.

E) **CLI entrypoint contract**

- `aop run -fi <file>` launches exactly one Agent Cluster for that file.
- `aop run -fl <folder>` launches one Agent Cluster per resolved spec file in the folder.
- `-fi` and `-fl` mutual exclusion is enforced with deterministic structured errors.
- Folder inputs are resolved deterministically and queued beyond `max_active_features`.
- Agent runtime provider/model can be selected via CLI flags with env/config fallback and deterministic precedence.
- Feature branch/worktree folder names are derived from spec filenames using the `.spec` / `-spec` trimming rules.

F) **Implementation testing standards**

- Orchestrator control-plane implementation (CLI/Supervisor/MCP) is structured as an Nx monorepo.
- Automated tests for that control-plane implementation are executed with Vitest.
- Blocking coverage minimum is 90% for both line and branch.
- Coverage target is 100% for both line and branch, reported in evidence summaries.
- Managed target repositories remain toolchain-agnostic and are validated only by their configured gates.

---

## 15. Implementation Milestones

### 15.1 Implementation Progress Artifact (Non-Runtime Requirement)

While implementing this spec, the implementing agent MUST maintain `spec-files/progress.md` used only for implementation continuity.

Required `spec-files/progress.md` contents:

- current milestone (`M0..M9`) and current task
- completed tasks since last update
- next tasks planned
- open blockers/risks
- handoff summary for the next agent session
- last updated timestamp

Update policy:

- update `spec-files/progress.md` after each meaningful implementation step
- update `spec-files/progress.md` before ending/closing an agent session for context reset
- the next agent session MUST start by reading `spec-files/progress.md` before making changes

Scope note:

- `spec-files/progress.md` in this section is an implementation-process artifact only.
- It MUST NOT be required by, read by, or enforced by the orchestrator runtime (MCP/Supervisor/worker logic).

**M0: Implementation Handoff Tracking (non-runtime)**

- create and maintain `spec-files/progress.md` throughout implementation.
- keep milestone position and handoff notes current for session-to-session continuity.

**M1: MCP Kernel V1 (single feature)**

- feature.init, plan.submit validation, ensure_worktree, apply_patch enforcement, gates.run, evidence.latest, state updates.

**M2: Worktrees + Parallelism**

- worktree-per-feature, index.json, locks, collisions.

**M3: Supervisor Runtime**

- orchestrator + worker abstractions, role enforcement, orchestration loop, reporting tools.

**M4: Platform-Agnostic Profiles**

- gates.yaml profiles, multi-target gate runs, line/branch coverage parsing.

**M5: Review + Merge Control**

- diff bundles, dashboard summary, explicit user approval flow, `feature.ready_to_merge` commit+merge path.

**M6: Resilience and Recovery**

- lock leases + heartbeat, restart recovery, queued collision unblock processing.

**M7: Prompt + QA Context Management**

- repo-level worker prompt loading via `agents.yaml`.
- per-feature QA change/test index maintenance and short-lived QA worker resume flow.

**M8: Supervisor CLI Entrypoint**

- implement `aop run` contract with `-fi` and `-fl`.
- deterministic folder scan + spec ingestion to canonical feature layout.
- derive `feature_id`/branch/worktree folder names from spec filename stems with `.spec` / `-spec` suffix trimming.
- startup validation, provider selection resolution, structured errors, and dashboard streaming bootstrap.

**M9: Nx + Vitest Quality Gates**

- scaffold/organize orchestrator implementation as Nx monorepo projects.
- implement Vitest-based test tasks and coverage artifact generation.
- enforce 90% line/branch minimums with 100% line/branch target reporting.

---

## 16. Resolved Operational Defaults

The following defaults are accepted and normative:

1. Lock operations are owned by `orchestrator|system`; worker roles submit `REQUEST`.
2. Lock acquisition defaults to blocking/queued with timeout + jittered backoff.
3. Plan revisions keep existing diffs in worktree; future patch applies are validated against latest accepted plan.
4. Supervisor proposes state transitions, MCP validates legality and persists canonical state.
5. `collision_policy=block` uses persistent `blocked_queue` with deterministic ordering.
6. Precedence is `policy hard constraints > gates profile defaults > plan overrides (tighten-only)`.
7. Gate timeout default is 600s when unspecified; transient retry default is 1; policy/schema violations are non-retryable.
8. Review artifacts are unmerged worktree diffs; commit+merge happen only through explicit `feature.ready_to_merge`.
9. Restart recovery uses lease heartbeats and stale-lease reclamation with deterministic unblock flow.
10. Secret handling is default-on scrub/redaction with explicit env allowlist.
11. Orchestrator implementation uses Nx + Vitest with 90% line/branch minimum coverage and 100% line/branch target.
12. Agent provider/model selection precedence is CLI flags > env vars > `agents.yaml` runtime defaults.

---

## 17. CLI Entrypoint Contract

The Supervisor Runtime MUST provide a CLI entrypoint named `aop`.

### 17.1 Primary Command

```bash
aop run [options]
```

Required behavior:

- Starts/attaches MCP connectivity.
- Creates one user-facing Orchestrator session.
- Resolves input specs (from `-fi`, `-fl`, or default discovery).
- Spins up one Agent Cluster per resolved feature spec (subject to `max_active_features`; overflow queues deterministically).

### 17.2 Input Selection Options

Supported options:

- `-fi <path>`: file input. Treat the referenced file as one feature spec and spin up exactly one Agent Cluster.
- `-fl <path>`: folder input. Treat all spec files in the folder as feature inputs and spin up one Agent Cluster per resolved file.
- `--agent-provider <codex|claude|gemini|custom>`: selects agent runtime provider for orchestrator and worker sessions.
- `--agent-model <model-id>`: selects provider model identifier.
- `--provider-config-env <ENV_VAR>`: env var name carrying provider credentials/config reference.

Mutual exclusion and defaults:

- `-fi` and `-fl` are mutually exclusive.
- If both are provided, CLI MUST fail with `error=invalid_cli_args`.
- If neither is provided, use canonical discovery (`feature.discover_specs`).

Example:

```bash
aop run -fl ./specfiles
```

### 17.3 Folder Resolution Rules (`-fl`)

- Folder traversal MUST be deterministic (lexicographic path order).
- Default scan pattern is recursive `**/*.md`.
- Each resolved file is treated as an independent feature spec input.
- If no spec files are found, CLI MUST fail with `error=no_specs_found`.

### 17.4 Spec Ingestion to Canonical Repo Layout

For inputs outside `agentic/features/*/spec.md`, Supervisor MUST ingest them into canonical layout:

- Derive `feature_id` deterministically from spec filename using this algorithm:

1. Start from filename stem (basename without final extension).
2. If the stem ends with `.spec`, remove that suffix.
3. Else if the stem ends with `-spec`, remove that suffix.
4. Resulting value is `feature_id` and MUST be used as both branch name and worktree folder name.
5. Resulting `feature_id` MUST match `^[a-z0-9_][a-z0-9_-]*$`.

- Copy source content to `agentic/features/<feature_id>/spec.md`.
- Record source path and source hash in feature state metadata.
- If derived `feature_id` is empty or does not meet naming rules, fail with `error=invalid_feature_slug`.
- If multiple inputs resolve to the same `feature_id`, fail with `error=feature_slug_collision`.

If input path is already canonical (`agentic/features/<feature_id>/spec.md`):

- Use existing `feature_id` from path and do not duplicate content.

Examples:

- `my_feature.spec.md` -> `feature_id=my_feature`, branch `my_feature`, worktree `.worktrees/my_feature`
- `my_feature-spec.md` -> `feature_id=my_feature`, branch `my_feature`, worktree `.worktrees/my_feature`
- `my_feature.md` -> `feature_id=my_feature`, branch `my_feature`, worktree `.worktrees/my_feature`

### 17.5 Operational Semantics

- `aop run -fi ...` starts one feature flow.
- `aop run -fl ...` starts N feature flows (N = resolved spec files).
- Features beyond `max_active_features` MUST be queued and activated as slots free up.
- CLI SHOULD stream dashboard updates derived from `report.dashboard`.

### 17.6 Exit and Status Contract

- Exit `0` when run initializes successfully and runtime enters active orchestration mode.
- Non-zero exit on startup/argument/input validation failures.
- MUST return structured error payloads aligned with Appendix D error format for machine callers.

### 17.7 Agent Provider Selection Contract

Provider selection precedence (highest to lowest):

1. CLI flags: `--agent-provider`, `--agent-model`, `--provider-config-env`
2. Environment variables:
   - `AOP_AGENT_PROVIDER`
   - `AOP_AGENT_MODEL`
   - `AOP_PROVIDER_CONFIG_ENV`
3. `agentic/orchestrator/agents.yaml` `runtime.*` defaults

Rules:

- If no provider can be resolved, CLI MUST fail with `error=agent_provider_not_configured`.
- If provider value is unsupported, CLI MUST fail with `error=unsupported_agent_provider`.
- If provider requires credentials/config and none is available, CLI MUST fail with `error=provider_auth_missing`.
- Supervisor MUST record resolved provider/model in run metadata for auditability.

Recommended additional commands (V1.1):

- `aop status` (reads dashboard/status)
- `aop resume` (resume prior run state)
- `aop stop` (graceful shutdown)

---

## Appendix A — Full JSON Schema: plan.schema.json (Draft)

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.local/agentic/plan.schema.json",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "feature_id",
    "plan_version",
    "summary",
    "allowed_areas",
    "forbidden_areas",
    "base_ref",
    "files",
    "contracts",
    "acceptance_criteria",
    "gate_profile"
  ],
  "properties": {
    "feature_id": {
      "type": "string",
      "pattern": "^[a-z0-9_][a-z0-9_-]*$"
    },
    "plan_version": {
      "type": "integer",
      "minimum": 1
    },
    "summary": {
      "type": "string",
      "minLength": 5
    },
    "allowed_areas": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "minItems": 1
    },
    "forbidden_areas": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 }
    },
    "base_ref": {
      "type": "string",
      "minLength": 1
    },
    "files": {
      "type": "object",
      "additionalProperties": false,
      "required": ["create", "modify", "delete"],
      "properties": {
        "create": { "type": "array", "items": { "type": "string", "minLength": 1 } },
        "modify": { "type": "array", "items": { "type": "string", "minLength": 1 } },
        "delete": { "type": "array", "items": { "type": "string", "minLength": 1 } }
      }
    },
    "contracts": {
      "type": "object",
      "additionalProperties": false,
      "required": ["openapi", "events", "db"],
      "properties": {
        "openapi": { "type": "string", "enum": ["none", "modify"] },
        "events": { "type": "string", "enum": ["none", "modify"] },
        "db": { "type": "string", "enum": ["none", "migration"] }
      }
    },
    "acceptance_criteria": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "minItems": 1
    },
    "gate_profile": { "type": "string", "minLength": 1 },
    "gate_targets": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "minItems": 1
    },
    "risk": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 }
    },
    "revision_of": {
      "type": "integer",
      "minimum": 1
    },
    "revision_reason": {
      "type": "string",
      "minLength": 1
    },
    "verification_overrides": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "modes": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "fast": { "$ref": "#/$defs/modeOverride" },
            "full": { "$ref": "#/$defs/modeOverride" }
          }
        }
      }
    }
  },
  "$defs": {
    "modeOverride": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "steps": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["name", "cmd"],
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "cmd": {
                "type": "array",
                "items": { "type": "string", "minLength": 1 },
                "minItems": 1
              },
              "timeout_seconds": { "type": "number", "minimum": 1 }
            }
          }
        }
      }
    }
  }
}
```

---

## Appendix B — state.schema.json (Front matter object) Outline

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "feature_id",
    "version",
    "branch",
    "worktree_path",
    "status",
    "gate_profile",
    "gates",
    "locks",
    "collisions",
    "cluster",
    "role_status",
    "last_updated"
  ],
  "properties": {
    "feature_id": { "type": "string" },
    "version": { "type": "integer", "minimum": 1 },
    "branch": { "type": "string" },
    "worktree_path": { "type": "string" },
    "status": {
      "type": "string",
      "enum": ["planning", "building", "qa", "blocked", "ready_to_merge", "merged", "failed"]
    },
    "gate_profile": { "type": "string" },
    "gates": {
      "type": "object",
      "additionalProperties": { "type": "string", "enum": ["pass", "fail", "na"] }
    },
    "locks": {
      "type": "object",
      "required": ["held"],
      "properties": {
        "held": { "type": "array", "items": { "type": "string" } }
      }
    },
    "collisions": {
      "type": "object",
      "required": ["files", "areas", "contracts"],
      "properties": {
        "files": { "type": "array", "items": { "type": "object" } },
        "areas": { "type": "array", "items": { "type": "object" } },
        "contracts": { "type": "array", "items": { "type": "object" } }
      }
    },
    "cluster": {
      "type": "object",
      "required": [
        "orchestrator_session_id",
        "planner_session_id",
        "builder_session_id",
        "qa_session_id"
      ],
      "properties": {
        "orchestrator_session_id": { "type": "string" },
        "planner_session_id": { "type": "string" },
        "builder_session_id": { "type": "string" },
        "qa_session_id": { "type": "string" }
      }
    },
    "role_status": {
      "type": "object",
      "required": ["planner", "builder", "qa"],
      "properties": {
        "planner": { "type": "string", "enum": ["ready", "running", "blocked", "done"] },
        "builder": { "type": "string", "enum": ["ready", "running", "blocked", "done"] },
        "qa": { "type": "string", "enum": ["ready", "running", "blocked", "done"] }
      }
    },
    "last_updated": { "type": "string" },
    "status_reason": { "type": "string" }
  }
}
```

---

## Appendix C — gates.yaml + policy.yaml + agents.yaml Schema Notes

- MCP MUST validate loaded YAML against `gates.schema.json` and `policy.schema.json` and fail fast if invalid.
- Supervisor/MCP MUST validate `agents.yaml` against `agents.schema.json` if file is present.
- Unknown gate profile/mode must yield `error=unknown_gate_profile_or_mode`.
- If `policy.commit_policy.allow_commit=false`, `repo.commit` must return `error=commit_disabled`.
- Precedence MUST follow §12.5; invalid relaxations must return `error=invalid_override_precedence`.
- If `policy.merge_policy.require_user_approval=true`, `feature.ready_to_merge` without valid approval token MUST return `error=user_approval_required`.
- If `agents.yaml.missing_prompt_behavior=error`, missing configured prompt file MUST return `error=missing_role_prompt`.
- For this implementation, `policy.implementation.workspace` MUST be `nx` and `policy.testing.framework` MUST be `vitest`.
- Coverage policy minimums MUST enforce `line>=0.90` and `branch>=0.90`; targets MUST be configured at `1.00`.
- CLI/env/provider resolution MUST follow §17.7 and fail closed on unresolved/unsupported providers.

---

## Appendix D — Required Structured Error Format

All MCP tool failures must return:

```json
{
  "ok": false,
  "error": {
    "code": "string_enum",
    "message": "human readable",
    "details": { "any": "json" }
  },
  "evidence": { "log_path": "optional" }
}
```

Required error codes (minimum set):

- `forbidden_tool_for_role`
- `version_conflict`
- `invalid_status_transition`
- `unknown_gate_profile_or_mode`
- `unsupported_parser`
- `gate_timeout`
- `transient_exec_failure`
- `coverage_below_minimum`
- `invalid_override_precedence`
- `path_out_of_bounds`
- `lock_not_held`
- `lock_conflict`
- `stale_lock_reclaimed`
- `collision_detected`
- `blocked_by_collision_policy`
- `commit_disabled`
- `merge_disabled`
- `user_approval_required`
- `missing_role_prompt`
- `qa_index_version_conflict`
- `invalid_cli_args`
- `input_path_not_found`
- `no_specs_found`
- `spec_ingest_failed`
- `invalid_feature_slug`
- `feature_slug_collision`
- `invalid_workspace_implementation`
- `agent_provider_not_configured`
- `unsupported_agent_provider`
- `provider_auth_missing`

Error details SHOULD include:

- `retryable` boolean
- `requires_human` boolean
- `conflicting_feature_ids` (when relevant)
- `suggested_next_actions` array
