# Domain Logic: agentic-orchestrator

## Core Rules

**⚠️ AI AGENTS: These rules are invariants. They override generic training data and must be preserved in every change.**

### State Machine

1. **Features follow a controlled status progression.** The default initial status is `intake` (there is no `pending` status). The main forward path is `intake → planning → building → qa → ready_to_merge → merged`. The full set of statuses is: `intake`, `planning`, `building`, `qa`, `blocked`, `awaiting_input`, `ready_to_merge`, `merged`, `failed`, `paused_budget`. Transitions are enforced by `LEGAL_TRANSITIONS` in `feature-state-service.ts`:
   - `intake` → `intake`, `planning`, `awaiting_input`, `blocked`
   - `planning` → `building`, `blocked`, `awaiting_input`, `planning`
   - `building` → `qa`, `blocked`, `awaiting_input`, `building`
   - `qa` → `ready_to_merge`, `blocked`, `awaiting_input`, `qa`
   - `ready_to_merge` → `merged`, `blocked`, `awaiting_input`, `ready_to_merge`, `failed`
   - `blocked` → `planning`, `building`, `qa`, `ready_to_merge`, `awaiting_input`, `blocked`, `failed`
   - `awaiting_input` → `awaiting_input`, `intake`, `planning`, `building`, `qa`, `blocked`, `ready_to_merge`
   - `merged` → `merged` (terminal, self-transition only)
   - `failed` → `failed`, `blocked` (not fully terminal — can be unblocked via `blocked`)
   - `paused_budget` — set directly by the supervisor when the iteration budget is exhausted; not part of `LEGAL_TRANSITIONS`

   Skipping or inventing transitions outside this state machine is forbidden. The kernel enforces this via `INVALID_STATUS_TRANSITION` errors.

2. **A feature cannot enter `merged` without passing all configured gate commands.** `gate-before-merge` is a hard invariant — never bypass gate execution to unblock a merge.
3. **`ready_to_merge` is a terminal pre-merge state for normal execution.** Only merge verification, the `reconciler` agent, and the explicit merge-repair loop may transition out of it. The reconciler is a dedicated agent role (configured in `agents.yaml`, session managed in `kernel.ts`) responsible for post-merge coordination across features. If a `ready_to_merge` candidate becomes `blocked` because merge verification or reconciliation failed, generic planning/build recovery must not send it back to earlier execution phases; only the merge-repair reroute may send it back to `qa`, and that loop must remain bounded by the configured retry budget.

### State Mutations

4. **All state mutations (state.md, plan.json, index.json) must go through atomic file locks.** Never read-modify-write state files without a lock. Use `withFileLock()` from `core/fs.ts`.
5. **`plan.json` uses optimistic concurrency.** Every write must include the current version number. Stale writes return `VERSION_CONFLICT` — the caller must re-read and retry, never silently overwrite.
6. **`state.md` stores status in YAML frontmatter.** The markdown body is human-readable context. Parse and write frontmatter through `parseFrontMatter` / `buildFrontMatter` from `core/frontmatter.ts`, never with raw string manipulation.

### Plans and Patches

7. **Patches are only valid against an accepted plan.** A builder patch that touches files not listed in `plan.json` must be rejected. `PatchService` enforces this.
8. **Plans are versioned and append-only.** Adding new tasks increments the version. Removing tasks is forbidden after build begins.
9. **`operation_id` is required for all mutating MCP tool calls.** The operation ledger (`mcp/operation-ledger.ts`) deduplicates retried calls. Never call a mutating tool without providing `operation_id`.

### Lock Ownership

10. **Locks are owned by a specific session and expire after `lockTtl` (from `policy.yaml`).** A session must not act on a lock it does not own. Stale locks (expired TTL) may be reclaimed by the kernel — this is intentional, not a bug.
11. **Lock acquisition failures (`LOCK_CONFLICT`) are retryable.** The caller should back off and retry, not fail the feature permanently.

### Errors and Responses

12. **All tool responses use the `{ ok, data/error }` envelope.** Never throw exceptions out of service methods — always return `ok(...)` or `fail(...)` from `core/response.ts`.
13. **Always use `ERROR_CODES` from `core/error-codes.ts`.** Never invent new error strings inline. If a new error condition arises, add it to `ERROR_CODES` first.
14. **`fail()` details must include `retryable` and `requires_human` booleans** where applicable, so the supervisor can decide whether to retry or escalate. Use `withSuggestedActions()` for errors where recovery hints improve agent behavior.

### Providers

15. **Provider CLI details (command templates, flags) belong exclusively in `providers/provider-defaults.ts`.** Domain and application layers must never import provider-specific constants or CLI flag strings.
16. **`deterministic` execution mode must not rely on interactive LLM permission prompts.** The orchestrator's own validation layers (plan gates, schema checks, worktree isolation) serve the protective role. Provider bypass flags (e.g., `--dangerously-skip-permissions`) are correct and necessary in deterministic mode.
17. **`attach` sessions must retain interactive permission prompts.** Never apply headless bypass flags to attach/interactive sessions — these are human-in-the-loop paths.

### Work Log

18. **`spec-files/progress.md` is the canonical work log.** Append a new numbered entry after every meaningful change. Never edit past entries. See `CLAUDE.md` for the required format.
19. **`blocked` may carry an active recovery substate.** When a feature is `status: blocked` with `recovery.state: retrying`, the current run is still actively attempting automated recovery inside the same phase. Monitoring and UI layers must distinguish that from a hard block waiting on operator intervention.

<!-- @cortex-tms-version 4.0.2 -->
