<!-- task=m-agent-task-runtime-target-selector/11+10_unknown_isolation plan=9 tag=REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR -->

# Plan - Canonical retry handoff state machine과 재시작 원자성

## 이 파일을 읽는 구현 에이전트에게

구현과 검증을 끝낸 뒤 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 내용과 원문 출력으로 채운다. active 파일은 그대로 두고 review 준비 완료만 보고한다. 종결은 code-review skill 전용이다. 차단되면 정확한 원인, 시도한 명령/출력, 재개 조건만 구현 에이전트 소유 evidence에 기록한다. 사용자에게 질문하거나 user-input 도구를 호출하지 않고, control-plane stop file 생성, 다음 상태 분류, archive/log 이동, `complete.log` 또는 roadmap 수정은 하지 않는다.

## 배경

아홉 번째 리뷰에서 다른 existing PLAN을 가리키는 locator가 qualified retry로 승인되고, commit한 logical context가 dispatcher 재시작 시 ordinary resume commit에 의해 첫 invoke 전에 유실됨을 재현했다. retry marker 저장 실패도 in-memory state를 되돌리지 않으며, 제출 문서가 restart/save/exactly-once를 검증했다고 제시한 테스트들은 현재 소스에 없다. task-bound artifact identity와 locator-first durable handoff를 하나의 crash-safe state machine으로 닫는다.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_8.log`
- Prior review: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_8.log`
- Verdict: `FAIL`
- Required:
  - retry validator가 locator의 existing `plan_path`를 현재 task PLAN과 비교하지 않아 다른 PLAN evidence를 승인한다.
  - prepared continuation은 retry flag가 꺼진 재시작에서 ordinary resume commit으로 지워지고, locator-before-consume/handoff identity가 없으며 retry-marker save fault도 exact rollback하지 않는다.
  - actual terminal, invalid artifact matrix, save fault, commit-before-invoke/locator-before-consume restart, exactly-once와 정상 sibling 전체 state 회귀가 없고 제출 evidence에 존재하지 않는 테스트가 기록됐다.
- Suggested: 없음
- Nit: 없음
- Affected files: `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`, `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- Verification evidence: focused 4개, dispatcher/context 24개, branch drain 6개, `py_compile`, `git diff --check`는 통과했다. 계획의 dedicated fault/restart test는 `AttributeError`; persisted-state 재현은 mismatched PLAN 승인=`True`, restart context=`None`, retry save fault exact pre-state=`False`였다. 전체 256개 중 task 범위 밖 SKILL contract 1개가 실패했다.
- Roadmap carryover: `throughput-policy`, SDD S06/S09/S11의 task-local qualified failover, persisted logical continuation, same-batch snapshot과 unknown 격리를 유지한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md)
- Task ids:
  - `throughput-policy`: target별 정적 cap 없이 ready task dispatch와 batch quota snapshot 재사용
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_8.log`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_8.log`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md`
- `agent-roadmap/sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md`
- `agent-contract/index.md` — dispatcher에 매칭되는 별도 계약 문서 없음.
- `agent-spec/index.md` — dispatcher에 매칭되는 living spec 없음.
- `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/10+09_target_cap_removal/complete.log`

### SDD 기준

- SDD는 `agent-roadmap/sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md`, 상태 `[승인됨]`, 잠금 `해제`다.
- 대상 Evidence Map은 S11 → `throughput-policy`이며, retry variant는 S06 logical context와 S09 persisted decision/transition을 함께 보존해야 한다.
- 따라서 체크리스트와 검증은 current PLAN identity, task-local persisted transition/context, locator-first exactly-once consume, same-batch/unknown sibling isolation을 한 actual dispatcher transaction에서 관찰한다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 matching `agent-test/local/testing-smoke.md`를 읽었다.
- 변경은 dispatcher state transaction과 격리 integration simulation이다. Edge/Node dev script, mock E2E, live provider preflight는 앱 실행 경로를 바꾸지 않고 실제 provider/CLI/subprocess를 금지하는 이번 범위에는 적용하지 않는다.
- deterministic fake runner와 subprocess-deny guard, fresh Python unittest, `py_compile`, `git diff --check`를 사용한다. 전체 suite의 현재 task 범위 밖 `ReviewControlTest.test_final_channel_contract_is_top_level_and_singular` 실패는 별도 후보로 유지하되 이 task의 추가 실패는 허용하지 않는다.

### 테스트 커버리지 공백

- `validate_retry_artifact_evidence`는 current PLAN equality가 없고 existing test는 workspace/task/PLAN/raw/normalized mismatch matrix를 실행하지 않는다.
- persisted continuation은 commit 후 재시작에서 지워지며 locator에 source handoff identity를 쓰거나 matching pending을 consume하는 회귀가 없다.
- `mark_retry_quota_refresh` direct mutation과 transition/attempt state save fault의 in-memory/on-disk exact equality가 없다.
- pre-loop terminal은 helper/accessor만 확인하고 actual branch를 호출하지 않으며, 정상 sibling도 전체 relevant state deep equality가 아니다.

### 심볼 참조

- rename/remove는 필수가 아니다.
- 영향 호출은 `StateStore.mark_retry_quota_refresh` → `dispatch_with_store`, `validate_retry_artifact_evidence`, `persisted_execution_decision` → `commit_execution_decision`, `run_worker` → `run_escalating` → `invoke`와 대응 테스트다.
- 내부 signature를 바꾸면 `rg --sort path`로 위 production/test 호출을 모두 갱신한다. selector 공개 JSON schema와 외부 API는 바꾸지 않는다.

### 분할 판단

- split policy를 평가했다. canonical evidence admission, decision/context commit, locator durable write와 pending consume는 하나의 crash-window state machine이며 각 조각을 별도 PASS하면 깨진 중간 상태를 승인한다.
- production과 최소 회귀는 같은 dispatcher/StateStore 소유 경계이고 외부 API·별도 도메인 rollout이 없다. 기존 single subtask `11+10_unknown_isolation`을 유지하는 것이 안전하다.
- 디렉터리 의존성 `+10`은 `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/10+09_target_cap_removal/complete.log` 단일 후보로 충족되었다.

### 범위 결정 근거

- selector policy/matrix와 공개 JSON, global quota cache, target semaphore, reverse failover, generic stderr inference, roadmap/SDD/rules, dispatcher SKILL narrative와 unrelated dirty docs/WORK_LOG은 수정하지 않는다.
- 전체 suite의 `SKILL.md` final-channel contract 실패는 현재 두 파일의 retry handoff 범위 밖이므로 별도 task 후보다. 이번 task는 해당 실패를 숨기지 않고 전체 출력에 정확히 기록한다.
- `agent-contract`와 `agent-spec`에는 matching 문서가 없어 갱신하지 않는다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`; route-free scope/evidence snapshot만 평가했다.
- build/review closure는 모두 `true`: 정확성 조건과 영향 호출이 확정됐고 temp workspace, fake runner, save-fault injection으로 재현 가능하며 사용자 결정·외부 권한이 없다.
- build `route_basis=local-fit`, `capability_gap=none`, grade scores=`scope_coupling:1,state_concurrency:2,blast_irreversibility:1,evidence_diagnosis:2,verification_complexity:1`, grade floor=`none`.
- build loop-risk는 ordered states 6개(blocked evidence→retry intent→prepared decision/context→committed handoff→attempt locator→consumed), actor 2개(dispatcher/restarted worker와 StateStore), component 5개(StateStore/validator/selector/worker/invoke), locator JSON·prompt assembly, artifact validity×save phase×restart window×target×sibling 축으로 `temporal_state`, `concurrent_consistency`, `boundary_contract`, `structured_interpretation`, `variant_product`가 matched되고 `triggered=true`다.
- review `route_basis=official-review`, `capability_gap=none`, grade scores=`1/2/1/2/1`, grade floor=`none`.
- finalizer=`finalize-task-policy.sh`, `finalizer_mode=pair`; build=`local-G07` / `PLAN-local-G07.md`, review=`cloud-G07` / `CODE_REVIEW-cloud-G07.md`, official target=`codex/gpt-5.6-sol xhigh`.

## 구현 체크리스트

- [x] retry admission이 현재 `Task.plan`과 locator workspace/task/PLAN/raw/normalized artifact를 exact-match하고 marker state 변경도 save fault에서 exact rollback한다.
- [x] prepared continuation을 독립 durable handoff로 보존하고 restart에서 재수화하며, 첫 attempt locator에 handoff identity/source context를 기록한 뒤 matching pending handoff만 원자적으로 exactly once consume한다.
- [x] actual terminal/artifact/fault/restart 회귀가 모든 알려진 variant와 정상 sibling 전체 state 불변을 provider/CLI/subprocess 없이 실행하고 실제 명령 원문만 evidence로 남긴다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Task-bound retry identity와 atomic admission

- 문제: `dispatch.py:1007-1015`는 locator PLAN이 absolute existing file인지만 확인해 다른 PLAN도 승인한다. `:595-649`는 여러 task state를 직접 바꾼 뒤 한 번 save하므로 실패 시 in-memory mutation을 복원하지 않는다.
- 해결 방법:
  - initial task scan의 canonical `Task`를 retry admission에 전달하고 validator가 `task.plan.resolve()` 및 workspace/task/attempt artifact를 fail-closed exact-match하게 한다.
  - retry 대상 state 변경은 touched key 존재 여부와 deep value를 snapshot한 StateStore transaction으로 적용해 save 실패 시 전체 touched task를 복원한다.
  - Before (`dispatch.py:1014`):
    ```python
    if not plan_path.is_absolute() or not plan_path.is_file():
        return False
    ```
  - After:
    ```python
    if task.plan is None or plan_path.resolve() != task.plan.resolve():
        return False
    store.update_tasks_atomically({task: retry_values})
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: task-bound validator, initial scan handoff와 multi-key exact rollback transaction을 구현한다.
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: malformed/missing/mismatched workspace/task/PLAN/raw/normalized 및 admission save fault table을 추가한다.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants`가 current PLAN exact identity, marker 부재, same-target resume와 on-disk/in-memory equality를 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants -v` — 모든 artifact/save variant PASS.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2] Durable locator-first continuation handoff

- 문제: `dispatch.py:1710`에 저장한 context는 `:1731-1792`의 restart resume commit에서 `None`으로 교체되고 `run_worker:4798`에 도달하기 전에 사라진다. `invoke:3160-3202` locator에는 handoff identity/source context와 matching consume가 없다.
- 해결 방법:
  - retry intent와 prepared continuation을 별도 상태로 표현하고 stable `handoff_id`, work unit, source locator, previous/next target, prompt artifact identity를 저장한다.
  - 재시작에서 committed handoff가 있으면 decision/history를 중복 commit하지 않고 pinned decision/spec/context를 재수화한다.
  - 첫 attempt locator를 handoff metadata와 함께 durable write한 뒤 `active_locator`와 matching pending consume를 한 StateStore transaction으로 적용한다. consume save 실패면 process를 시작하지 않고 pending pre-state를 보존하며, locator-before-consume 재시작은 같은 handoff를 복구한다.
  - Before (`dispatch.py:1792`):
    ```python
    commit_execution_decision(..., pending_continuation=prepared_context)
    ```
  - After:
    ```python
    handoff = prepare_or_rehydrate_continuation(...)
    persist_attempt_then_consume_handoff(store, task, locator_path, handoff)
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: durable handoff schema, restart rehydration, locator-first matching consume와 non-duplicate history를 구현한다.
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: commit-before-invoke, locator-before-consume, transition save와 attempt consume save fault를 재현한다.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest.test_retry_commit_fault_atomicity_and_restart_context`가 exact prompt/context, handoff id, history/decision exactly once, key 존재 여부를 포함한 state/file equality와 subprocess 0회를 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_commit_fault_atomicity_and_restart_context -v` — 모든 crash/save window PASS.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3] Actual dispatcher closure evidence

- 문제: `test_dispatch.py:9238-9367`은 actual terminal을 호출하지 않고 invalid artifact 축을 생략한다. `:9371-9527`은 restart/save fault가 없고 sibling 일부 필드만 비교하며 active review에 기록된 5개 테스트도 소스에 없다.
- 해결 방법:
  - actual pre-loop와 loop-exhausted `run_escalating` 분기를 실행해 canonical last failure/locator/selected/work-unit을 비교한다.
  - `dispatch_with_store→run_worker→invoke` fake seam으로 commit restart와 locator restart를 실행하고 first prompt/attempt handoff/consume 1회 및 normal sibling entire state deep equality를 비교한다.
  - test loader에서 실제 존재하는 테스트명과 실행 count를 원문 그대로 기록하고, 전체 suite의 out-of-scope known failure를 다른 성공으로 재작성하지 않는다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: actual terminal, artifact, fault/restart/exactly-once/sibling matrix를 명시된 test method로 구현한다.
  - [ ] `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/CODE_REVIEW-cloud-G07.md`: 실제 stdout/stderr/exit code만 채운다.
- 테스트 작성: 작성. dedicated fault/restart 테스트와 `ThroughputQuotaBatchTest`/`DispatcherCanonicalFailoverIntegrationTest` 통합 회귀가 각 invariant를 직접 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest ThroughputQuotaBatchTest DispatcherCanonicalFailoverIntegrationTest -v` — actual branch/transaction 회귀 PASS, external provider/CLI subprocess 0회.

## 의존 관계 및 구현 순서

1. item 1에서 retry admission identity와 save transaction을 고정한다.
2. item 2에서 그 identity를 소비하는 durable handoff state machine을 구현한다.
3. item 3에서 모든 crash window와 sibling isolation을 actual dispatcher 경로로 닫는다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1, REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2 |
| `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1, REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2, REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3 |

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants -v` — task-bound artifact/save variants PASS.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_commit_fault_atomicity_and_restart_context -v` — durable commit/restart/consume variants PASS.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest ThroughputQuotaBatchTest DispatcherCanonicalFailoverIntegrationTest -v` — actual terminal/context/throughput PASS, external subprocess 0회.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py BlockerDrainTest -v` — 독립 branch drain PASS.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'` — task 범위의 추가 실패 없음. 현재 checkout의 out-of-scope `ReviewControlTest.test_final_channel_contract_is_top_level_and_singular`만 남으면 exact failure/exit 1을 숨기지 않고 기록하며, 병행 변경으로 해결됐으면 전체 PASS를 기록한다.
- `python3 -m py_compile agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` — exit 0.
- `git diff --check` — 출력 없음, exit 0.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다.
