--- name: orchestrate-agent-task-loop description: Run agent-task work and autonomously execute active PLAN/CODE_REVIEW loops on request. Use when dispatching dependency-ready work in parallel by predecessor completion and workspace write claims, running lane/G-specific Codex, Claude, agy, and Pi workers, adding Pi self-checks, converging official Codex reviews, and escalating cloud context until the task loop finishes. --- # Orchestrate Agent Task Loop ## ๐Ÿšจ ABSOLUTE PRIORITY โ€” NEVER SEND `final` EXCEPT IN THE TWO CASES BELOW > [!CAUTION] > **This section overrides every success, blocker, exit-code, error-handling, and termination rule below.** > > **Never send on the `final` channel or end the caller turn unless at least one of the two titled permissions below applies. Never infer another exception from a lower section or runtime condition.** ### `final` Permission 1 โ€” Verified Successful Completion Allow `final` only after every condition below is true: - Every user-defined completion condition is satisfied. - Every observed task in every in-scope task group has a verified archived `complete.log`. - Every generated `WORK_LOG.md` is archived as `work_log_N.log`. - No active pair or running, pending, or blocked task remains. - The final dispatcher exit code is `0`. ### `final` Permission 2 โ€” Explicit User Instruction to Stop This Run Allow `final` when the user explicitly instructs the caller to stop the current run and return through `final`. ### Persistent-Run Instructions Revoke Successful-Completion Permission If the user says โ€œdo not stop,โ€ โ€œnever send final,โ€ โ€œkeep going,โ€ or gives an equivalent persistent-run instruction, verified success alone does not permit `final`. Only an explicit user instruction to stop the current run or return through `final` releases this restriction. ### Every Other User-Visible Message Must Use `commentary` Use only the `commentary` channel for every user-visible message before `final` is permitted. This includes status, partial success, completion candidates, blockers, failures, questions, apologies, waits, retries, and recovery guidance. Partial success, FAIL/WARN, USER_REVIEW, a blocker, retry exhaustion, timeout, a tool error, plan-generation failure, dispatcher exit code `2` or `3`, child exit, loss of a session/cell, and context compaction never permit `final`. Dispatcher stdout streamed directly by the execution layer is tool output, not a caller-authored message. Never spend an LLM turn restating, summarizing, or relaying a routine dispatcher event. ### Child Prompt Text Never Grants Caller `final` Permission The prompt-contract phrase `Final in Korean.` controls only the child model response language. It never authorizes the caller to use the `final` channel. ## Purpose Monitor the file-based state contract under `agent-task/` and converge the workflow from ready PLAN implementation through official code review and follow-up PLANs. Let the script determine filenames, dependencies, slots, and session locators; let each CLI agent make semantic implementation and review decisions. Treat Korean text inside code spans or fenced examples as exact runtime or file-contract literals. Keep all surrounding instructions in English, and never translate those literals unless the runtime contract changes. ## Inputs - `workspace`: Trusted repository root containing `agent-task/` (optional; defaults to the current directory). - `task_group`: Name of a specific `agent-task/` to run (optional). - `dry_run`: Inspect state, routes, and dependencies without starting a CLI (optional). - `max_parallel`: Non-negative integer cap on unique active task-stage attempts across the physical workspace. Omission defaults to `3`; explicit `0` is unlimited. `--task-group` does not narrow occupancy, adopted external attempts count, internal helper coroutines do not count separately, and an override must be supplied again after restart. - `retry_blocked`: Explicitly retry the same PLAN blocked by a previous dispatcher run in non-dry-run mode (optional). With `task_group`, reset only that group's blockers and 10-attempt counters while preserving other group state. ## Preconditions - [ ] Read the current state contracts in `agent-ops/skills/common/plan/SKILL.md` and `agent-ops/skills/common/code-review/SKILL.md`. - [ ] Verify that `codex`, `claude`, `agy`, and `pi` are on PATH and their login/provider configuration is valid. - [ ] Limit automatic approval to PLAN execution inside the current workspace; do not expand scope to external-system changes or destructive work. - [ ] Verify that no other dispatcher is running in the same workspace. Never bypass a workspace-lock failure. - [ ] Run `--dry-run` before the first live run to inspect active-task classification and dependency state. ## Routing Contract | PLAN route | Worker | |---|---| | `local-G01`โ€“`local-G06` | Pi `iop/ornith:35b`, thinking high | | `local-G07`โ€“`local-G08` | KST day/night agy `Gemini 3.6 Flash (High)` โ†’ Pi `iop/glm-5.2`, thinking high | | `local-G09`โ€“`local-G10` | Claude `claude-opus-4-8`, effort xhigh | | `cloud-G01`โ€“`cloud-G02` | Codex `gpt-5.3-codex-spark` โ†’ agy `Gemini 3.6 Flash (Low)` โ†’ Pi `iop/glm-5.2`, thinking low | | `cloud-G03`โ€“`cloud-G04` | agy `Gemini 3.6 Flash (Medium)` โ†’ Pi `iop/glm-5.2`, thinking medium | | `cloud-G05`โ€“`cloud-G06` | agy `Gemini 3.6 Flash (High)` โ†’ Pi `iop/glm-5.2`, thinking high | | `cloud-G07`โ€“`cloud-G08` | Claude `claude-opus-4-8`, effort xhigh | | `cloud-G09`โ€“`cloud-G10` | Codex `gpt-5.6-sol`, reasoning xhigh | | Every `CODE_REVIEW-*` | Codex `gpt-5.6-sol`, reasoning xhigh | Concurrency limits: - Global physical-workspace limit: omitting `max_parallel` caps execution at `3`; explicit `max_parallel=0` is unlimited. A positive value caps unique active task-stage attempts and is not narrowed by `task_group`. The cap applies across worker, self-check, review, and verified external-active attempts in the same physical workspace. - Pi `ornith:35b`: 3. - agy: 1. - Official Codex review: no separate review-only limit; subject to the global cap. - Run worker/self-check and official review in parallel only when they belong to different dependency-ready tasks and their canonical PLAN write sets do not collide in the current physical workspace. Prevent duplicate execution of the same task. - Even with `complete.log`, treat an explicit predecessor as unfinished while live model/review execution evidence for that task remains. Delay only its consumers; do not propagate the delay to dependency-free siblings or other task groups. - Run official reviews for different dependency-ready tasks with disjoint workspace claims in parallel. - Before the first review batch, normalize the Agent-Ops-managed `.gitignore` block once so reviews do not concurrently modify the same shared control file. - Require exactly one valid, non-empty `Modified Files Summary` (and legacy `์ˆ˜์ • ํŒŒ์ผ ์š”์•ฝ`) in the active or recovery PLAN. Fail the task closed when any path is broad, outside the workspace, a directory, malformed, or missing. - Atomically claim every canonical modified-file path before admitting worker, self-check, or review. A collision is a runtime wait, not a predecessor dependency. Retain the task's claim through every stage, retry, dispatcher restart, and follow-up PLAN; replace or expand its own claim only when the new set does not collide, and release it only after verifying the completed archive. - Scope write claims to the canonical physical workspace. Separate worktrees and clones use independent state and may run in parallel; task-group filtering never narrows the claim ledger inside one workspace. ## Prompt Contract Keep control prompts in English, insert absolute paths only, and do not expand these sentences unnecessarily. - A dispatcher child runs only while `IOP_AGENT_TASK_EXECUTION_ID` is present. - Prefix every worker and review prompt with: `You are a child agent already launched by the dispatcher, not the orchestration caller. Execute only the assigned role directly. Do not start, monitor, or wait for orchestration through dispatch.py or orchestrate-agent-task-loop. You may run dispatch.py --validate-plan only when required by plan or code-review finalization because that mode validates one candidate PLAN without starting or monitoring orchestration.` - Keep local self-check prompts short. Start fresh self-check and recovery prompts with: `Think in English. Final in Korean.` The same-session unchecked-item retry uses the exact terse prompt below. - Cloud worker: `Read {PLAN_PATH} and complete the task. Keep artifact content in English. Final in Korean.` - Pi worker: `Think in English. Keep artifact content in English. Final in Korean. Read {PLAN_PATH} and complete the task.` - Pi self-check full pass: `Think in English. Final in Korean. Read {PLAN_PATH}; review all work once, fix omissions, and update {CODE_REVIEW_PATH}. Keep files in English.` - Pi self-check unchecked-item retry: `The code review file is incomplete! Complete it now!` - Official review: `Read {CODE_REVIEW_PATH} and start the review. Keep artifact content in English. Final in Korean.` - Review-exit recovery: `Continue the review for {TASK_PATH}. Keep artifact content in English. Final in Korean.` - Context escalation: `Continue from {LOCATOR_PATH}. Check the saved context and current workspace. Keep artifact content in English. Final in Korean.` Never ask a worker, self-check, or review model to create, edit, or summarize `WORK_LOG.md`. Do not treat Pi self-check exit code `0` as success by itself. Set `selfcheck_done=true` only when `## Implementation Checklist` (or legacy `## ๊ตฌํ˜„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ`) in `CODE_REVIEW_PATH` contains at least one Markdown list checkbox and every `[...]` checkbox value has at least one non-whitespace character. If both canonical and legacy checklist headings are present in the same file, fail closed. Accept any non-empty value, including `x`, `v`, and `โœ…`. Do not inspect `## Implementation Item Completion`, `Deviations from Plan`, `Key Design Decisions`, `Verification Results`, or final CODE_REVIEW synchronization text. Run the full self-check prompt exactly once. If its checklist condition fails, resume that successful pass's Pi native session and run the unchecked-item retry prompt up to 10 times. Each retry must resume the locator returned by the preceding successful pass so the same conversation context is preserved; never repeat the full review prompt or start a fresh retry session. Persist the latest successful context locator for dispatcher restart, and block instead of starting fresh when that context cannot be resumed. Block that task after the 10th unchecked-item retry remains incomplete, and continue draining independent work. After an AGY/Gemini worker exits `0`, apply the same `CODE_REVIEW_PATH` implementation-checklist regex before accepting worker completion. If it is incomplete, run a fresh quota probe: only an `exhausted` target becomes `provider-quota` and enters the existing selector failover/promotion chain; `available` or `unknown` remains a completion-evidence recovery on Gemini. For Pi worker recovery attempts, pass only `Read {PLAN_PATH}. Continue.` without a locator explanation. Pi self-check recovery must preserve the current full-pass or unchecked-item role and use its concise prompt. For other CLI escalation attempts, pass `Continue from {LOCATOR_PATH}. Check the saved context and current workspace. Keep artifact content in English. Final in Korean.` Preserve the collaboration prohibition and next-state-materialization sentence in official-review escalation and recovery prompts. Do not ask the model to write a separate handoff summary. When recovering a KST-night `local-G07`โ€“`local-G08` Laguna locator or a terminal `session-stall` locator left by an earlier dispatcher, first require the locator and native session to belong to the current physical workspace. Do not create a fresh session ID for an owned locator. Resume its native session file with `pi --session` and the existing `--session-dir`. For worker recovery pass `Think in English. Keep artifact content in English. Final in Korean. Continue this session and complete the current task.` For interrupted full self-check recovery pass `Think in English. Final in Korean. Continue. Keep files in English.` For an unchecked-item retry, pass its normal concise prompt while resuming the existing native session. After a dispatcher restart, find the owned locator and resume the same session. Count this same-session restart toward the same stage's 10-consecutive-failure limit. ## Work-Log Contract - Keep exactly one `agent-task/{task_group}/WORK_LOG.md` per task group. Do not create one in a split-subtask directory. - Allow only the dispatcher to modify this file. Worker/self-check/review models need not read or update it, and success must not depend on its prose. - Append chronological `START`/`FINISH` rows with time, task, loop, role, attempt, model, result, and locator. In `task`, record the active role artifact relative to `agent-task/`: the PLAN path for a worker and the CODE_REVIEW path for self-check/review. In `loop`, record the PLAN identity's zero-based `plan` number (`0` is the initial plan). Record time in KST (`UTC+09:00`) as `YY-MM-DD HH:MM:SS`, for example `26-07-26 07:40:15`. Use this single timeline to inspect parallel execution order. - Do not require the common code-review skill to preserve `WORK_LOG.md`. For split work the group log normally remains in the parent because review moves only the selected subtask. For a single task review may move the log with the task archive; after review exits, resolve exactly one source from the active group path or verified completed archive and normalize it to `work_log_N.log`. - After every observed task in a task group has a verified complete archive and no active/running task remains, append the final `FINISH` and move the generated `WORK_LOG.md` under the final completed archive's group root as `work_log_N.log`. If an archive exists after restart but the last `START` lacks `FINISH`, do not terminate or archive while any PID/start token, per-attempt process marker, or pidless stream/native evidence remains live. Track it until execution evidence has ended and the complete archive is verified, then append `FINISH` with `reconciled:verified-complete-archive` and move the log. Use `agent-task/archive/YYYY/MM/{task_group}/` for split tasks and the actual suffix-bearing archive destination for a single task. Set `N` to one more than the maximum suffix for the same task group across all months, starting at `0`. - If `WORK_LOG.md` archiving fails or multiple active/archive sources exist, drain other independent work and return non-terminal exit `3` for retry. Return successful exit `0` only after a completed group that generated a log has no active `WORK_LOG.md` and its `work_log_N.log` is verified. Keep an incomplete group's `WORK_LOG.md` active for blocker or exit `3` recovery. - Split each attempt locator into `stream.log` for model stdout/stderr and `heartbeat.log` for dispatcher state. Determine health only from the newest progress in `stream.log` and native session events; never use heartbeat mtime as progress evidence. Do not copy either log into `WORK_LOG.md`. - Keep child stdout/stderr, normalized model output, and periodic heartbeat records in locator-owned logs only. The dispatcher's user-visible stdout is an event stream and must never mirror model stream lines or heartbeat ticks. - If locator refresh temporarily fails after an attempt starts, do not terminate a live model process or start a duplicate task. Record a warning, keep monitoring, and preserve error evidence at the next successful refresh. - After verifying a PASS archive's `complete.log` and confirming no live execution evidence for that task, delete all of its attempt directories, including locators, native sessions, `stream.log`, `heartbeat.log`, and CLI auxiliary logs. Do not delete them while a model process or conservatively active pidless stream/native evidence remains. Treat transient deletion failure as non-terminal exit `3` for the next reconciliation without blocking the completed task or other tasks; do not return successful exit `0` while any attempt directory remains. Preserve failed or blocked attempt logs as recovery evidence. - Record log-creation or append failure in the locator as `work-log-setup` or `work-log-runtime-write` and block the task. - Exclude dispatcher-authored `WORK_LOG.md` changes from official-review progress/stagnation signatures. Count only real changes in PLAN/CODE_REVIEW, review logs, and the write-set. ## Caller Lifecycle and Status Display - **ABSOLUTE RULE โ€” Do not stop the whole task group when a task-local blocker appears.** Delay only the blocked task and consumers that require its incomplete result as a predecessor. Keep the caller turn active until every independent ready/running task finishes. - **ABSOLUTE RULE โ€” Scan the complete new-task candidate set only on initial dispatcher entry and immediately after creating a verified `complete.log`.** After a worker/self-check/review attempt ends or a task changes stage, reclassify only that task. After `complete.log` is created, immediately start every runnable task except currently running tasks in the same pass. Another task's execution, wait, dependency, review, or recovery state must not block a candidate. If no candidate or running task remains and only blockers and their dependent waits remain, exit with code `2`. - Treat the dispatcher as the execution lifecycle and observation owner. It performs deterministic health checks, recovery, retries, routing, and state transitions without caller-LLM supervision. The caller owns only launch authorization, intervention after an attention event, and the `final` gate. - Keep the caller turn suspended and launch the dispatcher as one persistent foreground execution. Use execution-layer event waiting or direct stdout streaming; never use an LLM-generated polling turn as a keepalive. Never start a duplicate dispatcher while the child is live. - Never wrap the dispatcher in `timeout`, a short `wait_for`, or an arbitrary cancel/terminate wrapper. Tool yield or expiration of a response window is not process termination. Resume the same execution-layer wait without commentary, analysis, or inspection. - **ABSOLUTE RULE โ€” The caller never monitors.** During normal execution or event silence, do not run a timer loop, periodically poll through the model, or inspect `ps`, dispatcher `--dry-run`, `state.json`, locator files, `stream.log`, `heartbeat.log`, or `WORK_LOG.md`. A tool yield, empty wait, routine lifecycle event, or response-window expiration does not permit caller-LLM involvement. - Stream routine lifecycle banners directly from dispatcher stdout to the user without routing them through the caller LLM. Routine events include starts, deterministic retries/recovery, waits, per-task review results, per-task completion while other work remains, and any event for which the dispatcher has already selected the next action. - Wake the caller LLM only for an attention event that the dispatcher cannot resolve autonomously: a verified `USER_REVIEW` decision, an exhausted terminal blocker, an unrecoverable state/log contract error, loss of the execution handle that requires targeted recovery, or terminal dispatcher exit. A warning or automatic retry is not an attention event merely because it reports an error. - No dispatcher output, an empty wait, or a wait-window expiration is normal event silence. It never permits `final`, caller termination, a duplicate dispatcher, a state inspection, or a model wake-up. Keep the execution-layer wait attached with the longest supported window. - A lost session/cell exists only when the execution layer reports the tracked identifier unavailable or aborted, or reports the child process exited; a normal wait return alone is insufficient. Then perform exactly one reinspection of active tasks, locators, PIDs, and state. If that snapshot proves a live dispatcher owner, do not inspect it again until an attention event is observed. Resume event waiting from the same session/cell when available; otherwise subscribe from EOF to only newly appended START/FINISH rows in the task-group WORK_LOG.md. If the fallback observer itself ends without an event while the dispatcher remains live, reattach the same EOF-only observer without reading any prior row or inspecting state. A routine START/FINISH row or direct output only confirms the subscription and does not permit model wake-up or state inspection. Only a dispatcher exit, explicit attention event, fallback-observer error, or explicit user request permits the next targeted inspection. Exit code `0` is successful terminal state. Exit code `2` is a drained blocker or explicit persistent-state-error terminal state. Exit code `3` is a non-terminal tracking state, including another dispatcher workspace lock, a live external agent, or an unexpected dispatcher interruption; inspect PID, locator, and state only after that event. - On a scheduler/control-plane exception or unexpected exception in an individual agent coroutine, do not immediately freeze it as a task blocker or let the dispatcher event loop cancel other running agents and child processes. Monitor every independent running agent until natural completion, return non-terminal exit `3`, and let the next dispatcher reconcile file and state results. Even when the original exception is a persistent-state error, do not convert it to exit `2` if any agent was running. - In drained-blocker terminal state, persist the orchestration group as `blocked`, directly blocked tasks as `blocked`, consumers waiting on their predecessors as `waiting`, and verified independent completed tasks as `complete` in `.git/agent-task-dispatcher/state.json`. On re-entry, set incomplete observed tasks back to orchestration state `active`, then reevaluate actual task-local blockers and dependencies. - Persist observed tasks and the complete same-name archive baseline present at startup, regardless of `complete.log`, in `.git/agent-task-dispatcher/state.json`. If an active task disappears after child restart, recover completion only when exactly one new `complete.log` archive absent from the baseline exists; block when none or multiple exist. Do not count a late `complete.log` added to an incomplete archive that existed before execution as current-run completion. - If existing `state.json` cannot be read or validated as a JSON object, block the dispatcher. Never replace it with empty state or reset the 10-attempt budget. Repair or explicitly handle it before rerunning. - When a new user turn arrives, continue tracking the same overall request unless it explicitly cancels the previous request. - Let the execution layer display `์ž‘์—…์‹œ์ž‘`, `์ž๊ฐ€๊ฒ€์ฆ์‹œ์ž‘`, `๋ฆฌ๋ทฐ์‹œ์ž‘`, `๋ฆฌ๋ทฐ์žฌ์‹œ๋„`, `Pi๋ณต๊ตฌ์žฌ์‹œ๋„`, `์„ธ์…˜์‘๋‹ต๋ณต๊ตฌ์žฌ์‹œ๋„`, `์„ธ์…˜์—ฐ๊ฒฐ์žฌ์‹œ๋„`, `๋ฆฌ๋ทฐ๊ฒฐ๊ณผ`, `์ž‘์—…๋Œ€๊ธฐ`, `์ž‘์—…์ฐจ๋‹จ`, `๋””์ŠคํŒจ์น˜์ถ”์ ๋Œ€๊ธฐ`, and `์ž‘์—…์™„๋ฃŒ` directly from dispatcher stdout. Never duplicate them in model-authored `commentary`. Use `commentary` only when an attention event actually requires caller reasoning or a user decision. Event silence never grants `final`; only the two permissions in the absolute-priority section do. - Determine every CLI's health/progress primarily from actual stdout/stderr in `stream.log`, plus native session events when available. Before accepting PID, marker, native-session, or stream evidence, require the locator path and recorded workspace identity to belong to the current physical workspace; accept an identity-less legacy locator only under the current store's `runs` root. Never use heartbeat mtime as progress evidence. Record workspace id, dispatcher PID, agent PID, each process start token, and the per-attempt process environment marker in the locator; namespace that marker by workspace. Another dispatcher must not start a duplicate attempt merely because the stream is quiet when the PID/start token or marker shows the same process is alive. For a locator without an agent PID, never infer stale state or duplicate recovery from elapsed time while any stream/native progress evidence exists; use only an actual terminal error or confirmed process exit as recovery evidence for every model. Run Pi with `--mode json` so `thinking_delta`, `text_delta`, and tool streams reach stdout. End an **exact** Pi toolCall-to-all-toolResult interval only when every `toolCall.id` in the preceding assistant event matches a later `toolResult.toolCallId`; never terminate the process on a time limit. If the locator lacks an agent PID during this interval, never classify it as stale or duplicate recovery based on log age; require recorded process evidence to show termination. Do not infer tool execution from `starting`, `unknown`, model reasoning, or post-toolResult state. Outside this interval, use only `stream.log` updates for Pi liveness; toolResult alone does not reset the model-response silence clock. If the stream stops for three minutes outside tool execution, store the final stream excerpt as `pi_silence_inspection` for Pi or `stream_silence_inspection` for another CLI, emit `๋ชจ๋ธ์‘๋‹ต์ ๊ฒ€`, and do not terminate the model process. Recover only from an actual terminal error or process exit. - Detect a local-model `repetition-loop` only when the same normalized chunk repeats three consecutive times with no new tool event or file/state change. Do not infer it from similarity or semantic duplication in `thinking_delta`/`text_delta`. This signal alone must not terminate the process, block the task, trigger recovery/retry, or escalate the model; keep observing for substantive progress or an actual terminal error. - Keep `provider-connection`, `provider-stream-disconnect`, `session-stall`, `generic-error`, `process-terminated`, context/quota/model errors, and review-control violations distinct, but make them share a budget of 10 consecutive automatic recovery failures for the same task stage. On the 10th failure, block that task and do not auto-resume after cooldown. Reset the stage counter after success. - Record an explicit terminal blocker when the initial Pi full self-check plus 10 same-context unchecked-item retries leave the implementation checklist incomplete, or official review makes no change 10 consecutive times. - While one task recovers or becomes blocked, continue every ready/running task that neither requires it as a predecessor nor collides with its retained workspace claim. Internal recovery or blocking must not trigger an arbitrary complete-candidate rescan. - If review shared-state preflight fails, block only ready review tasks and still start every worker/self-check with a disjoint claim in the same pass. The complete scan after `complete.log` must preserve the existing snapshot rather than reread already running task directories, avoiding races with parallel archive moves that could stop another process. - For KST-night `local-G07`โ€“`local-G08` Laguna locator `context-limit`/`session-stall`, prefer the Prompt Contract's same-session resume and display `Pi์„ธ์…˜์—ฐ์†์žฌ์‹œ์ž‘`. Use a fresh session and `์„ธ์…˜์‘๋‹ต๋ณต๊ตฌ์žฌ์‹œ๋„` only for other legacy Pi `session-stall` recovery. - Do not stop for user review based on filename alone. Recognize a `user-review` terminal blocker only when the active task's `USER_REVIEW.md` contains `์ƒํƒœ: USER_REVIEW`, exactly one supported type, a concrete target, non-`์—†์Œ`/`๋ฏธ์ •` blocker rationale, unresolved user actions or decisions, and resume conditions that prevent the next safe implementation step. For `milestone-lock`, require a real `agent-roadmap/**/milestones/*.md` target. For `external-execution`, require an exact runner/device/service/access target and evidence that no authorized automatic executor can perform the required verification. If the form is incomplete or conflicts with active PLAN/CODE_REVIEW, block it as a task-state contract error instead. - Recognize `## Code Review Result` (with `Overall Verdict: PASS|WARN|FAIL`) or legacy `## ์ฝ”๋“œ๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ` (with `์ข…ํ•ฉ ํŒ์ •: PASS|WARN|FAIL`) as the review verdict. If both canonical and legacy headings are present in the same file, fail closed. Never parse the same string in implementation evidence, command output, or example text as the runtime verdict. - Locator/raw logs under `.git/agent-task-dispatcher/runs/` are internal recovery state and may not appear in the normal project tree. Include the `locator=` path emitted when the dispatcher starts an attempt and the task-group `WORK_LOG.md` path in status updates. - If a specified `task_group` has neither an observed active task nor a persisted completed task, return state error `unobserved-task-group` with exit code `2`; never treat it as empty completion. - If child failure is recoverable inside the repository, continue within the 10-attempt budget. After draining independent work, report a blocker that the caller cannot clear in the current turnโ€”such as exhausted budget, required user decision, or external permissionโ€”with its path, evidence, and resume condition. ## Failure Classification and Reporting Contract - Record dispatcher PID, actual agent PID, import time, source path, import-time SHA-256, attempt-start current SHA-256, and `dispatcher_source_matches_loaded` in every attempt locator. Every failure banner and subsequent status must present the locator's exact `failure_class`, `failure_source`, `provider_transport_failure_confirmed`, `dispatcher_pid`, `agent_pid`, `dispatcher_source_sha256`, source-match state, and `locator`; never summarize them into a broader cause. - A running Python dispatcher does not hot-reload source edits. If `dispatcher_source_matches_loaded=false`, do not claim that new rules are active. Report the loaded/current hashes and execution-version difference until the process-owning session can safely exit and restart. - Use `provider-connection` or `provider-stream-disconnect` only when original CLI terminal diagnostics contain a strong provider pattern in provider/backend/SSE context. Do not infer provider failure from `connection refused`, `dial tcp`, or `curl` peer failure in ordinary tool/test stderr. For a confirmed attempt, preserve `failure_source=provider-terminal-diagnostic`, `provider_transport_failure_confirmed=true`, `failure_evidence_source`, and `failure_evidence_excerpt` in the locator. - Treat legacy locator `session-stall` as a record of an earlier dispatcher timeout policy, not as provider failure. During recovery, report `failure_source=dispatcher-timeout`, `provider_transport_failure_confirmed=false`, `termination_initiator=dispatcher`, and the original timeout phase/seconds. Never let the current dispatcher create a new silence timeout. - Record a SIGTERM-family termination not initiated by the dispatcher as `process-terminated`, with `failure_source=process-termination` and `termination_initiator=unknown`. Never classify exit code `143` as provider failure without actual provider terminal evidence. - Do not generalize one `pi -p` fresh/isolated session attempt to a Pi TUI or system-wide provider outage. Describe a system-level provider outage only with additional controlled reproduction using the same command, model, and prompt, or backend-health evidence. - Count `process-terminated` in the same per-stage consecutive-failure budget as other automatic-recovery classes. On the 10th consecutive failure, block that task; never reset the budget after cooldown or auto-resume. A shared budget does not imply common causation or establish provider-failure evidence. ## Procedure 1. **Inspect state.** - Print active tasks, routes, stages, and dependencies: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --dry-run ``` - Treat `NN_...` as immediately eligible. Treat `NN+PP[,QQ...]_...` as eligible only after each predecessor's `complete.log` is found once in the active or narrow archive lookup for the same task group and predecessor execution evidence has ended. - Never infer an implicit dependency from numeric order alone. 2. **Run the dispatcher.** - Run all active tasks with the default physical-workspace cap of `3`: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py ``` - Run one task group: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --task-group ``` - Cap total concurrent attempts across the physical workspace: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --max-parallel 2 ``` - Explicitly disable the cap: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --max-parallel 0 ``` - Preview classification without launching CLIs under the same cap: ```bash python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --dry-run --max-parallel 2 ``` - If a worker/self-check/review future ends without `complete.log`, reread only that task and run its next stage. Do not rescan the complete candidate set. - Persist `active_stage` for a running task. After dispatcher restart, exclude that task from candidates, restore or conservatively adopt its workspace write claim, and immediately dispatch every other dependency-ready task whose claim does not collide. - **ABSOLUTE RULE:** Scan the complete candidate set only at initial entry and immediately after creating a verified `complete.log`. In that scan, exclude tasks shown as running by current-workspace state and native session/locator evidence, then atomically admit every dependency-ready task with a non-colliding write claim. An unmet dependency or write collision excludes only that task. Exit instead of polling when no candidate remains. - Persist Pi worker success, Pi self-check success, and official review as separate stages. If restart state is `worker_done=true` and `selfcheck_done=false`, resume on the same Pi model, not with worker or review. Run the full pass when `selfcheck_incomplete=0`; otherwise resume the persisted successful self-check context locator with an unchecked-item retry. Never replace a missing or invalid persisted context with a fresh session. - Key persistent state to the first-line `task/plan/tag` generation and, for `m-*`, its `milestone-task` scope. Checklist/body edits to the same PLAN do not reset the stage; a new plan number or changed Milestone Task scope does. - Send an already completed review stub with no dispatcher execution record to review. Never send dispatcher-recorded Pi worker success to review before self-check completes. - Start official review and worker/self-check together when they belong to different dependency-ready tasks with disjoint workspace claims. Wait for a claim owner to reach verified completion before admitting a colliding task. - Let the dispatcher record every worker/self-check/review attempt start and finish in the task-group `WORK_LOG.md`. - Archive `WORK_LOG.md` as `work_log_N.log` only after the final task review process exits, the dispatcher appends `FINISH`, and a complete scan finds no active/running task in that group. Accept the log at either the active group path or the verified completed single-task archive; do not impose either location contract on common plan/code-review. 3. **Escalate and recover context.** - For every route that lists Gemini followed by Pi GLM, classify terminal provider errors or stderr evidence of context/output limits, provider quota/rate limits, unavailable models, or confirmed provider transport errors as a qualified failover to that next GLM candidate. For AGY, accept top-level `error`, `fatal`, `request.failed`, or `turn.failed` events; failed/rejected status with a top-level error/code; stderr; or strong `RESOURCE_EXHAUSTED`, HTTP 429, quota, or rate-limit evidence in `agy-cli.log`. For Claude, classify a `rate_limit_event` with `rate_limit_info.status=rejected`, an error `result` with `api_error_status=429` or `error=rate_limit`, or a `You've hit your session limit ยท resets ...` terminal diagnostic as `provider-quota`. Cloud-only escalation remains `Claude -> Codex`; never escalate from an assistant message, source text, tool/test output, or a plain quota-configuration string in an AGY log. - Target Codex `gpt-5.6-terra` with reasoning `high` when escalating from Claude to Codex. - If Codex returns the same error, retry in a fresh Codex session using the locator while preserving the previous Codex model/reasoning and sharing the same stage's 10-consecutive-failure limit. Continue dispatching other tasks during recovery. - When current source reads a locator blocked 10 times as `generic-error` by older dispatcher source, collapse those 10 failures into one terminal error and clear only that task's blocker only if all 10 terminal-evidence records for the same task/plan/role/source/execution target reclassify to the same escalatable error. Include `stream.log` and the attempt's `agy-cli.log` for AGY. Do not adjust automatically when any history is missing or mixed, or when the locator dispatcher source hash equals the current source hash. Dry-run must display this escalation recovery and next model without writing state. Live execution must choose the higher target from the locator's actual failed target, not the initial PLAN route, inherit locator context, and restore the same escalation target and locator from persisted reclassification metadata after immediate restart. - Recover timeout, crash, process termination, permission, and ordinary implementation errors on the same target within the same stage's 10-consecutive-failure limit, preserving the actual failure class and locator. At exhaustion, block only that task and keep dispatching independent work. - On success after escalation, record `worker_cli` and `worker_model` from the successful locator's actual target, not the initial PLAN route. - Never escalate Pi to a cloud model. - Use attempt identity `__p____aNN` and namespace the process marker with the physical workspace id. Record canonical workspace root/id, CLI/model/reasoning effort, PLAN/review, `WORK_LOG.md`, session ID, native session path, and raw output log in the locator. - Store locators under repository `.git/agent-task-dispatcher/runs/`. Fall back to `${XDG_STATE_HOME}/agent-task-dispatcher//runs/` only when `.git` state is unwritable. 4. **Converge review.** - Run every official review in an independent Codex one-shot session with no separate numeric limit. Dispatch all ready reviews with disjoint workspace claims in parallel. - For finalization recovery without an active PLAN, recover the review target and write claim from the archived plan log for the same first-line generation metadata, including `milestone-task` when present. Keep the claim until the completed archive is verified. - Forbid collaboration/sub-agent tools in official review and finish inside the current one-shot session. If such a tool call appears, clean up that attempt's independent subprocess group and retry in a fresh review session. Count the failure toward the same stage's 10-consecutive-failure limit. - Delegate PASS archive, WARN/FAIL follow-up pairs, and review-finalization recovery to the `code-review` file contract. - Reclassify any remaining active pair and send it to worker or review. - Declare stagnation only when the plan write-set source snapshot and review/finding artifacts are all unchanged. Display `๋ฃจํ”„์ •์ฒด๊ฒฝ๊ณ ` and retry with backoff; on the 10th unchanged attempt, block that task as `review-no-progress-limit`. - Record a verified `USER_REVIEW.md`, dependency ambiguity, 10 repeated failures, or work-log setup/runtime-write failure only as that task's blocker. Delay only the blocker and consumers that depend on it; continue every independent ready/running task. Return drained terminal blocker exit code `2` only when no independent work remains. ## Verification Checklist - [ ] Scan the complete candidate set only on initial entry and immediately after verified `complete.log`; atomically claim and start every non-running, dependency-ready, non-colliding candidate in the same pass. - [ ] Confirm the actual CLI/model for each route matches the routing table. - [ ] Run exactly one full fresh-session self-check only for Pi work, followed by at most 10 unchecked-item retries in that same Pi native session context when its checklist remains incomplete. - [ ] Run every official review with Codex `gpt-5.6-sol` xhigh and dispatch dependency-ready reviews with disjoint workspace claims in parallel, subject to the global `--max-parallel` cap (no separate review-only limit). - [ ] Locate the native session and output log for every attempt locator. - [ ] Record every worker/self-check/review attempt `START`/`FINISH` in one task-group `WORK_LOG.md`. - [ ] For every completed task group that generated `WORK_LOG.md`, archive a `work_log_N.log` containing the final review `FINISH` and leave no active `WORK_LOG.md`. - [ ] Verify that a PASS task is archived and each newly released dependent task starts. - [ ] For success, verify every task's `complete.log`. For blocker exit, verify that no ready/running task remains and only task-local blockers and their dependent waits remain. - [ ] Verify dispatcher stdout contains lifecycle/attention events only; raw child output and heartbeat ticks remain in locator-owned logs and never require caller-LLM relay. - [ ] On blocking, output the task, reason, and locator. - If verification fails, stop the dispatcher and report only the cause without manually moving or overwriting active PLAN/CODE_REVIEW files. ## Output Format ```text ------------------------------------------ ์ž‘์—…์‹œ์ž‘: 03+01_event_contract_unit_tests ------------------------------------------ model=pi/iop/ornith:35b plan=/absolute/path/PLAN-local-G05.md work_log=/absolute/path/WORK_LOG.md ------------------------------------------ ๋ฆฌ๋ทฐ์‹œ์ž‘: 03+01_event_contract_unit_tests ------------------------------------------ model=codex/gpt-5.6-sol xhigh review=/absolute/path/CODE_REVIEW-local-G05.md ``` Use the same separator format for `์ž‘์—…๋Œ€๊ธฐ`, `์ž‘์—…์ˆ˜ํ–‰์ค‘`, `์ž๊ฐ€๊ฒ€์ฆ์‹œ์ž‘`, `๋กœ๊ทธ๋ณด์™„์žฌ์‹œ๋„`, `๋ชจ๋ธ์Šน๊ฒฉ`, `๋ฆฌ๋ทฐ๊ฒฐ๊ณผ`, `๋ฃจํ”„์ •์ฒด๊ฒฝ๊ณ `, `์ž‘์—…์ฐจ๋‹จ`, `์ž‘์—…๋กœ๊ทธ์•„์นด์ด๋ธŒ`, and `์ž‘์—…์™„๋ฃŒ`. ## Prohibitions - Never print periodic heartbeat ticks or child model stdout/stderr to dispatcher stdout. Preserve them only in locator-owned logs. - Never reevaluate PLAN/CODE_REVIEW lane or G in the dispatcher or rename those files. - Never infer dependency from numeric order when no predecessor index is present. - Never scan the complete archive or read archive files outside dependency candidates. - Never ask a worker to perform official review, archive work, or create `complete.log`. - Never treat Pi self-check as official review. - Never depend on a model-authored handoff summary for context recovery. - Never treat a generic failure as token/quota failure and escalate it to a higher model. - Never resolve `USER_REVIEW.md` automatically or guess a user decision.