---
name: orchestrate-agent-task-loop
description: Execute dependency-ready PLAN and CODE_REVIEW task loops with workspace write claims, a runtime-injected agent/model catalog, deterministic target failover, and persistent recovery state.
---

# Orchestrate Agent Task Loop

## Final-channel gate

Do not end the caller turn through `final` until either:

- every in-scope task has a verified archived `complete.log`, every generated work log is archived, no task or execution remains active, and the dispatcher exits `0`; or
- the user explicitly asks to stop the current run.

Use `commentary` for non-terminal status, blockers, questions, recovery notices, and partial completion. If the user asked for a persistent run, successful completion alone does not release this gate.

## Purpose

Monitor the file-backed workflow under `agent-task/` and converge ready PLAN implementation, optional self-check, official review, follow-up PLAN, and archive completion. The dispatcher owns deterministic scheduling, recovery, target transitions, and runtime evidence. Child agents own implementation and review judgments within their assigned artifact.

## Inputs

- `workspace`: trusted repository root containing `agent-task/`; defaults to the current directory.
- `execution_catalog`: required runtime agent/model catalog path, supplied with `--execution-catalog` or `AGENT_TASK_EXECUTION_CATALOG`.
- `task_group`: optional `agent-task/<task_group>` scope.
- `dry_run`: inspect routes, dependencies, claims, and catalog validity without launching an agent.
- `max_parallel`: workspace-wide active task-stage limit; defaults to `3`; `0` means unlimited.
- `retry_blocked`: retry eligible blocked tasks without changing their catalog route history.

`--validate-plan` validates one PLAN without launching orchestration and therefore does not require an execution catalog.

## Preconditions

- Read the current plan and code-review contracts routed by `agent-ops/skills/common/router.md`.
- Obtain the execution catalog from the runtime or project layer. Common owns no default agent, model, provider, or route catalog.
- Run `--dry-run` before the first live execution.
- Never bypass the physical-workspace dispatcher lock.
- Keep automatic approval inside the current workspace and the PLAN's declared write set.

## Runtime catalog contract

The catalog root contains exactly `schema_version`, `targets`, and `routes`. It must cover `worker` and `review`, and each stage must define every `local-G01` through `local-G10` and `cloud-G01` through `cloud-G10` route.

Each target has:

- an opaque `agent` identity;
- an opaque `model` identity;
- `execution_class`: `local_model` or `cloud_model`;
- optional `selfcheck_required` boolean;
- `runtime.command`: a non-empty argv template executed without a shell;
- optional `runtime.resume_command`, `preflight_command`, `environment`, `session_path`, `native_session_monitor`, and `auxiliary_logs`;
- optional `runtime.output_format`: `text` or `jsonl`.

Command templates may use only `{agent}`, `{model}`, `{target_id}`, `{workspace}`, `{attempt_dir}`, `{session_id}`, `{resume_session}`, and `{prompt}`. The catalog must not embed repository secrets; environment values should refer only to runtime-provided non-secret configuration.

Each route owns its ordered `candidates` plus optional `rule_id`, `policy_priority`, and `reason_codes`. A route may use catalog-owned `windows` instead of a fixed candidate list; every window supplies an IANA timezone, start/end time, and candidates. Exactly one window must match.

Before work starts, the dispatcher:

1. loads and validates the entire catalog;
2. verifies exact route coverage and every target reference;
3. verifies each target command is executable;
4. runs an optional target `preflight_command` for live execution;
5. records the catalog source and SHA-256 revision in the decision.

A persisted decision is valid only while the injected catalog revision and selected target snapshot still match. Catalog changes fail closed instead of silently changing an active work unit.

## Selection and failover

- Initial execution selects the first candidate in the injected route.
- Resume pins the persisted target and route revision.
- The dispatcher never queries quota before admission and never accepts a quota snapshot as selector input.
- Classify actual terminal output after an attempt. `provider-quota`, `context-limit`, `model-unavailable`, `provider-stream-disconnect`, and `provider-connection` may advance to the next unused route candidate.
- In particular, a confirmed quota/rate-limit error advances directly to the next candidate. A plain mention of quota in source text, model prose, or non-terminal output is not sufficient evidence.
- `generic-error`, process termination, work-log failure, and review-control failure do not imply quota and do not change the selected target.
- Never use a hidden promotion table or provider-specific fallback. If no next catalog candidate exists, keep recovery within the stage budget or block the task with evidence.
- Transfer logical context using the prior locator, normalized output, raw stream, workspace, and PLAN. Use native resume only when both targets opt into the same catalog-declared native-session mechanism and the session belongs to the current workspace.

## Scheduling and write claims

- Admit every dependency-ready task whose canonical PLAN write set does not collide with another active claim.
- Require exactly one non-empty `Modified Files Summary` or supported legacy heading. Reject broad, malformed, directory, outside-workspace, or missing paths.
- Atomically claim canonical paths before worker, self-check, or review execution. Keep a task's claim across retries and follow-up PLANs; release it only after verified archive completion.
- Treat explicit predecessors as unfinished while matching live execution evidence exists, even if a `complete.log` is already visible.
- Apply `max_parallel` across the physical workspace, independent of task-group filtering. Do not count internal helper coroutines as agent slots.
- A blocker delays only that task and its dependency closure. Continue draining independent work.

## Prompt and child boundary

Prefix worker and review prompts with the dispatcher-child boundary that prohibits starting or monitoring another orchestration loop. A child may use `dispatch.py --validate-plan` only when its plan or review finalization requires it.

Prompts must include absolute artifact paths and instruct the child to follow the repository's language and output rules. Do not hardcode a programming language, human language, agent, model, or provider in common prompts.

Never ask a child to create, edit, or summarize `WORK_LOG.md`; that file is dispatcher-owned.

## Self-check

Run self-check only when the selected catalog target declares `selfcheck_required=true`. The completing decision, not a fixed agent identity or execution class, determines the requirement.

Accept self-check completion only when `## Implementation Checklist` or its supported legacy heading contains at least one checkbox and every checkbox has a non-empty value. Run one full pass, then resume the latest successful native context for at most 10 unchecked-item retries when the target supports native resume. Block instead of silently starting a new context when a required persisted context is unavailable.

## Runtime evidence and recovery

- Store each attempt under the dispatcher state directory with `locator.json`, `stream.log`, `normalized-output.log`, and `heartbeat.log`.
- Record the target id, opaque agent/model identity, execution class, runtime contract, catalog evidence, process identity, workspace identity, timestamps, result, and exact failure evidence.
- Treat stderr as terminal diagnostic evidence. For JSONL, recognize generic terminal event fields such as error/fatal type or severity, rejected/failed status with an error code, and explicit error flags.
- Determine liveness from PID/start-token/process-marker evidence and actual stream or native-session progress. Heartbeat mtime is never agent progress.
- Never start a duplicate attempt while owned live evidence remains.
- Keep a 10-consecutive-failure budget per task stage. Reset only that stage's budget after success.
- Preserve failed attempt logs. Delete successful attempt logs only after verified archive completion and no live evidence.

## Work log

- Keep one dispatcher-owned `WORK_LOG.md` per task group.
- Append chronological `START` and `FINISH` rows with UTC time, task artifact, plan loop, role, attempt, selected agent/model display, result, and locator.
- Archive the group log as the next `work_log_N.log` only after every observed task in the group is verified complete and idle.
- Work-log write or archive failure is a retryable control-plane failure and prevents exit `0`.

## Invocation

```bash
python3 agent-ops/skills/common/orchestrate-agent-task-loop/scripts/dispatch.py \
  --workspace /absolute/repository \
  --execution-catalog /runtime/config/execution-catalog.json \
  --dry-run
```

Remove `--dry-run` to start execution. Add `--task-group <name>`, `--max-parallel <n>`, or `--retry-blocked` only when requested by the workflow.

Launch the live dispatcher as one persistent foreground process. Do not wrap it in an arbitrary timeout and do not start a second dispatcher after a normal tool yield. Wait on the same execution handle until an attention event or terminal exit.

## Completion checklist

- [ ] Catalog was injected, fully validated, preflighted, and revision-pinned.
- [ ] No fixed common agent/model/provider route or quota probe was used.
- [ ] Runtime quota errors moved only to the next catalog candidate.
- [ ] Dependencies, write claims, and workspace concurrency were enforced.
- [ ] Required self-check and official review stages completed.
- [ ] Every observed task has a verified archived `complete.log`.
- [ ] Work logs and successful attempt cleanup were reconciled.
- [ ] No active, waiting, pending, or blocked in-scope task remains.
- [ ] Dispatcher exited `0` before successful final response.
