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

# Plan - Durable retry continuation state와 fault-atomic commit

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

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

## 배경

여덟 번째 재검토에서 제출된 253개 테스트는 통과했지만 `{}` locator가 qualified retry intent로 승인되고, validated logical context는 decision commit 뒤 메모리 반환값에만 남아 재시작에서 유실됨을 확인했다. 강제 StateStore save 실패도 원래 없던 `quota_snapshot=None` key를 남겨 exact pre-state 계약을 깨뜨린다. retry evidence 검증, persisted decision/context handoff, 첫 attempt 인계와 fault rollback을 하나의 durable two-phase transaction으로 닫는다.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_7.log`
- Prior review: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_7.log`
- Verdict: `FAIL`
- Required:
  - non-empty 문자열만 확인해 `{}` locator도 qualified retry intent로 승인한다.
  - prepared logical context가 commit state에 저장되지 않고 retry context가 먼저 지워져 commit과 first invoke 사이 재시작에서 context가 유실되며, save rollback도 absent key를 `None`으로 남긴다.
  - 제출 회귀가 actual terminal 분기, invalid locator admission, save fault, commit-before-invoke restart와 정상 sibling 전체 state 불변을 실행하지 않는다.
- 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/13/1/6개와 fresh 전체 253개, `py_compile`, `git diff --check`는 PASS했다. 별도 persisted-state 재현은 invalid locator pending=`True`, failed commit exact pre-state=`False`, restart context=`None`을 확인했다.
- Roadmap carryover: `throughput-policy`, SDD S06/S09/S11의 task-local qualified failover, persisted transition/context, 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_7.log`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_7.log`
- `agent-ops/skills/project/orchestrate-agent-task-loop/SKILL.md`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.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-ops/rules/common/philosophy.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-ops/rules/common/rules-agent-spec.md`
- `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을 함께 보존해야 한다.
- 따라서 구현 체크리스트는 qualified locator identity, restart-safe logical context, task-local atomic state와 actual dispatcher evidence를 한 transaction으로 묶고 최종 검증에서 S06/S09/S11을 동시에 관찰한다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 matching `agent-test/local/testing-smoke.md`를 읽었다.
- 변경은 dispatcher persisted-state unit/integration simulation이며 Edge/Node user-flow, mock smoke, live provider preflight는 적용하지 않는다. testing domain rule에 따라 provider/session/network/CLI subprocess를 deterministic fake 또는 deny로 고정한다.
- 검증은 fresh Python unittest process, `py_compile`, `git diff --check`를 사용한다. repo 밖 runner, secret, endpoint, device, Docker와 external provider preflight는 필요하지 않다.

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

- `mark_retry_quota_refresh`는 locator JSON과 workspace/task/PLAN/raw/normalized artifact를 읽지 않아 malformed evidence를 pending으로 승인한다.
- commit은 logical context package를 durable state에 저장하지 않아 commit 이후 process restart가 context를 재수화하지 못한다.
- `StateStore.update_task` rollback은 key 존재 여부를 보존하지 않아 save fault 뒤 state deep equality가 깨진다.
- terminal test는 builder/accessor만 호출하고 pre-loop/loop-exhausted 실제 분기를 실행하지 않는다.
- actual dispatcher test는 success 한 경로만 실행하며 commit-before-invoke restart, selector/context/commit/save fault와 정상 sibling 전체 state deep equality가 없다.

### 심볼 참조

- rename/remove 없음.
- 영향 경계는 `StateStore.update_task`, `StateStore.mark_retry_quota_refresh`, `commit_execution_decision`, `persisted_execution_decision`, `build_context_package`, `invoke`, `run_worker`, `run_escalating`, `dispatch_with_store`와 대응 테스트다.
- 새 내부 retry/context helper는 위 호출 경계 안에서만 사용하고 selector 공개 JSON schema와 외부 API는 바꾸지 않는다.

### 분할 판단

- split policy를 먼저 평가했다. evidence validation, durable transition/context commit, attempt handoff와 rollback은 같은 StateStore transaction의 연속 단계라 분리하면 깨진 중간 상태를 독립 승인하게 된다.
- production과 regression도 같은 fault-window 계약으로 함께 검증해야 하며 소유권은 dispatcher 한 경계다. 기존 single subtask `m-agent-task-runtime-target-selector/11+10_unknown_isolation`을 유지한다.
- 디렉터리 의존성 `+10`은 `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/10+09_target_cap_removal/complete.log`로 충족되었다.

### 범위 결정 근거

- selector 공개 schema/policy/canonical target, global quota cache, target semaphore, reverse failover, generic stderr inference, roadmap/SDD/rules, unrelated dirty milestone/SDD와 parent `WORK_LOG.md`는 변경하지 않는다.
- dispatcher state에 필요한 내부 pending continuation/handoff schema와 fault tests만 수정한다. agent-contract와 agent-spec에는 매칭 문서가 없어 갱신하지 않는다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`; prior lane/grade/score/filename과 archive basename은 중립 평가 입력에서 제외했다.
- build/review closures는 모두 `true`: 범위는 두 파일의 retry transaction으로 닫혔고, 재현 출력과 deterministic fake suite로 검증 가능하며 사용자 결정·외부 권한은 없다.
- 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 5개(blocked evidence→retry intent→prepared transition/context→committed pending handoff→attempt/restart), actor 2개(scheduler/worker StateStore), component 4개(StateStore/selector/context/invoke), locator JSON assembly, validity×save×restart×target variant product로 `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`.

## 구현 체크리스트

- [ ] retry intent가 canonical worker decision/PLAN identity와 완전 일치하고 readable locator/workspace/task/PLAN/raw/normalized artifact가 유효할 때만 생성되며 invalid evidence는 marker 없는 same-target resume이 된다.
- [ ] validated context를 decision/history/quota와 함께 durable pending handoff로 commit하고 retry intent를 원자적으로 대체하며, attempt locator 인계·재시작·모든 save fault에서 context와 exact pre-state를 보존한다.
- [ ] actual terminal/dispatcher 회귀가 invalid identity/artifact, selector/context/commit/save fault, commit-before-invoke restart, first handoff exactly-once와 정상 sibling 전체 state 불변을 외부 호출 없이 검증한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Canonical retry artifact identity

- 문제: `dispatch.py:613`의 qualification은 `locator`가 non-empty인지 확인할 뿐이며 `test_dispatch.py:9104`의 `{}` locator도 `:9187`에서 pending으로 승인된다. terminal builder test도 실제 limit 분기를 호출하지 않는다.
- 해결 방법:
  - `dispatch_with_store`의 retry 초기화가 현재 `Task`와 workspace를 결합한 뒤 실행되게 하고, role/failure/selected/work-unit과 locator JSON의 workspace/task/PLAN/raw/normalized identity를 한 validator에서 fail-closed로 검증한다.
  - invalid/missing/mismatched evidence는 blocker와 budget을 명시적 retry semantics대로 정리하되 failover marker/context를 만들지 않고 persisted target resume으로 남긴다.
  - pre-loop와 loop-exhausted 실제 경로는 같은 canonical terminal builder를 호출하고 normalized last failure/locator를 기록한다.
  - Before (`dispatch.py:613`):
    ```python
    qualified = (... and isinstance(locator, str) and bool(locator.strip()) ...)
    ```
  - After:
    ```python
    retry_intent = validate_retry_terminal_evidence(workspace, task, blocker_evidence, worker_decision)
    qualified = retry_intent is not None
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: task-bound canonical evidence/artifact validator와 terminal 두 경로를 연결한다.
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: malformed JSON, missing/mismatched workspace/task/PLAN/raw/normalized와 terminal 두 실제 분기를 table test한다.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants`와 actual terminal-path test에서 invalid marker 부재, canonical record와 subprocess 0회를 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants -v` — 전부 PASS, 외부 subprocess 0회.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2] Durable context handoff와 exact rollback

- 문제: `dispatch.py:1714`의 context는 반환값에만 있고 `:1636` commit은 retry context를 지운다. commit 직후 restart는 `resume`과 `context=None`이 되며 `:541` rollback은 absent key를 `None`으로 남긴다.
- 해결 방법:
  - validated context package에 work-unit, previous/next target과 unique handoff identity를 포함하고 decision/history/quota와 같은 `update_task`에서 durable pending handoff로 저장하면서 retry intent를 대체한다.
  - `run_worker`/`invoke`는 restart에서도 matching pending handoff를 재수화한다. 새 attempt locator가 source context와 exact prompt/handoff identity를 먼저 durable하게 기록한 뒤에만 StateStore의 pending handoff를 active locator로 원자 consume한다. crash window에서는 pending state 또는 attempt locator 중 하나로 항상 복구 가능해야 한다.
  - `update_task`는 sentinel/deep snapshot으로 key의 존재 여부까지 복원하고 `mark_retry_quota_refresh` 및 transition/attempt consume save 실패도 in-memory/on-disk deep equality를 유지한다.
  - Before (`dispatch.py:1636`, `:1723`):
    ```python
    update_kwargs = {"retry_quota_refresh_context": None, ...}
    store.update_task(task, **update_kwargs)
    return decision, spec, prepared_context
    ```
  - After:
    ```python
    commit_execution_transition(..., pending_continuation=prepared_context)
    return decision, spec, persisted_pending_continuation(state)
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: exact rollback, persisted pending handoff, restart rehydration과 attempt locator consume를 구현한다.
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: selector/context/transition-save/attempt-save fault와 commit-before-invoke/locator-before-consume restart를 deep equality로 검증한다.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest.test_retry_commit_fault_atomicity_and_restart_context`가 모든 fault window와 exactly-once handoff를 table-driven으로 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_commit_fault_atomicity_and_restart_context -v` — 전부 PASS.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3] Contract-complete dispatcher restart regression

- 문제: `test_dispatch.py:9191`은 terminal helper만 검사하고 `:9349` success integration은 crash/restart와 save fault가 없으며 정상 sibling은 `:9362`의 selected 비교에 그친다.
- 해결 방법:
  - valid locator/stream/normalized fixture와 actual `dispatch_with_store→run_worker→invoke` seam을 유지해 commit 직후 restart와 attempt-locator handoff restart를 각각 재현한다.
  - blocked task의 entire pre-state를 fault 전후 deep equality로, 정상 sibling의 decision/quota/history/marker/context/active fields를 전체 deep copy로 비교한다.
  - first continuation prompt의 exact plan/locator/workspace/raw/normalized 필드, handoff identity, decision/history 1회, intent/pending consume 1회와 provider/CLI/subprocess 0회를 assert한다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: actual restart/fault/sibling matrix를 추가한다.
  - [ ] `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/CODE_REVIEW-cloud-G07.md`: 실제 구현·검증 원문 evidence를 완성한다.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest.test_retry_blocked_scopes_to_blocked_worker_and_refreshes_pinned_alternate`를 durable context와 full-state assertions로 확장한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest DispatcherCanonicalFailoverIntegrationTest -v` — 전부 PASS, external provider/CLI/subprocess 0회.

## 의존 관계 및 구현 순서

1. item 1에서 신뢰 가능한 retry evidence와 terminal identity를 고정한다.
2. item 2에서 context를 포함한 durable two-phase handoff와 rollback을 구현한다.
3. item 3에서 actual dispatcher fault/restart matrix로 두 경계를 함께 닫는다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1, 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_REFACTOR-1, REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2, REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3 |

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants -v` — canonical terminal/evidence 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` — fault-atomic commit/restart PASS.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest DispatcherCanonicalFailoverIntegrationTest -v` — actual dispatcher/context/throughput PASS, provider/CLI 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'` — fresh 전체 suite 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`의 구현 에이전트 소유 섹션을 채운다.
