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

# Plan - Retry handoff durable identity와 restart exactly-once 회귀

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

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

## 배경

retry handoff의 pending context는 실패 attempt locator를 보존하지만 새 worker attempt는 다른 locator를 기록한다. 현재 consume은 locator 문자열 동일성만 비교해 두 상태를 연결하지 못하므로 crash/restart에서 pending handoff가 다시 worker 실행을 유발할 수 있다. 실제 locator publication 경로를 통과하지 않는 focused test도 이 결함을 숨긴다.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_10.log`
- Prior review: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_10.log`
- Verdict: `FAIL`
- Required: retry context의 source locator와 새 attempt locator가 일치하지 않아 pending consume이 실패하며, dedicated restart test가 `restart_probe_calls=0`으로 실패한다.
- 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: two helper-level focused tests와 `BlockerDrainTest`는 PASS, dispatcher/throughput 30개 중 restart와 artifact identity가 FAIL, 전체 suite는 ReviewControlTest를 포함해 3개 FAIL이었다.
- Roadmap carryover: `throughput-policy`, SDD S06/S09/S11의 persisted continuation, logical context, same-batch quota snapshot을 유지한다.

## 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-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_10.log`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_10.log`
- `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/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 state에 매칭되는 계약 원문 없음.
- `agent-spec/index.md` — dispatcher에 매칭되는 living spec 없음.

### SDD 기준

- SDD는 `[승인됨]`, SDD 잠금은 `해제`다. Roadmap target `throughput-policy`는 S11 admission evidence를 요구한다.
- S06 `context-failover`와 S09 `dispatch-integration`의 persisted logical continuation/restart evidence가 handoff commit의 정확성 기준이다. S11은 sibling quota snapshot을 손상시키지 않는 회귀 범위를 정한다.
- Evidence Map S06/S09/S11에 따라 stable identity, retry resume, save-fault rollback, normal sibling 불변성과 deterministic dispatcher test를 같은 checklist와 최종 검증에 둔다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 `agent-test/local/testing-smoke.md`를 읽었다.
- dispatcher simulation은 fake subprocess/runner seam으로 provider CLI와 네트워크 호출을 금지한다. 이 Python state transaction은 Edge-Node dev script, mock smoke, live provider preflight 범위가 아니다.
- host Go는 `/config/.local/bin/go` → `/config/opt/go/bin/go`, `go1.26.2`, `GOROOT=/config/opt/go`로 확인했다. Python 검증에는 Go cache를 사용하지 않는다.

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

- helper 직접 호출은 matching locator consume과 save-fault rollback만 확인한다. 새 test는 실제 `invoke()` locator publication과 StateStore commit을 통과해 crash-after-write, save-fault, restart exactly-once를 검증해야 한다.
- `test_retry_restart_preserves_handoff_and_consumes_pending`는 현재 실패하므로 실제 dispatch preflight와 fake provider guard 아래에서 1회 probe/1회 invoke/0 subprocess를 다시 검증해야 한다.
- `test_retry_evidence_artifact_identity_variants`와 `ReviewControlTest` 실패는 이번 Required와 직접 관련 없는 기존 후보로 남기며, 전체 suite에서는 새 실패가 없는지만 확인한다.

### 심볼 참조

- 변경 후보: `StateStore.consume_matching_retry_handoff`, `StateStore.mark_retry_quota_refresh`, `invoke`, `run_escalating`, `run_worker`.
- 참조 위치는 `dispatch.py`와 `test_dispatch.py`뿐이며, 구현 뒤 `rg --sort path`로 전체 call site를 재확인한다.

### 분할 판단

- split 정책을 검토했다. durable state commit과 실제 process-free restart test는 같은 StateStore ownership과 하나의 exactly-once invariant를 공유한다.
- 별도 production/test child는 검증 가능한 중간 `complete.log`를 만들지 못하고 fake runner seam을 중복하므로 기존 `11+10_unknown_isolation` 단일 subtask가 안전하다. 신규 predecessor는 없다.

### 범위 결정 근거

- selector policy matrix, lane/G finalizer, quota schema, Edge/Node 코드, roadmap/SDD/rules, `WORK_LOG.md`, 그리고 unrelated dirty files는 제외한다.
- stale artifact-identity assertion의 정책 의미 변경과 ReviewControlTest는 별도 task 후보이며 이번 handoff transaction 수정에 포함하지 않는다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`, `finalizer=finalize-task-policy.sh`, `finalizer_mode=pair`.
- build/review closures는 모두 true: scope는 두 Python 파일로 닫혔고, source/test/SDD evidence와 deterministic commands가 있으며 외부 권한·결정이 없다.
- build `route_basis=local-fit`, `capability_gap=none`, scores=`1/2/1/1/1`, loop-risk는 state 5개, dispatcher+restart 2 actor, StateStore/invoke/run_worker 3 component, identity commit, crash×save×restart×sibling variants로 `temporal_state`, `concurrent_consistency`, `boundary_contract`, `variant_product` matched, `triggered=true`다.
- review `route_basis=official-review`, `capability_gap=none`, scores=`1/2/1/1/1`, floor=`none`.
- build=`PLAN-local-G06.md`, review=`CODE_REVIEW-cloud-G06.md`, official target=`codex/gpt-5.6-sol xhigh`.

## 구현 체크리스트

- [ ] retry handoff의 stable identity와 새 locator publication을 하나의 durable StateStore commit으로 연결해 crash/restart duplicate invoke를 막는다.
- [ ] 실제 `invoke()` publication, consume save-fault, restart를 provider subprocess 없이 통과하는 deterministic regression tests로 교체하고 focused/integration evidence를 갱신한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REFACTOR-1] Stable handoff commit

- 문제: `dispatch.py:581-583`은 context의 failed locator와 새 active locator가 같을 때만 consume한다. `:3083-3090`은 새 locator를 저장한 뒤 그 비교를 호출해 retry intent를 소비하지 못한다.
- 해결 방법:
  - `mark_retry_quota_refresh`가 만든 `handoff_id`를 retry context와 새 locator record에 보존한다.
  - locator JSON durable write 뒤 `active_locator`, pending flag, context를 handoff id로 검증해 한 번의 StateStore save로 전이한다. save 실패 시 메모리와 직렬화된 pre-state를 그대로 복원한다.
  - `run_worker`/`run_escalating`/`invoke`는 첫 retry attempt에만 handoff id를 전달하고 commit 뒤에는 재사용하지 않는다.
  - Before (`dispatch.py:3083-3090`):
    ```python
    write_json(locator_path, record)
    store.update_task(task, active_locator=str(locator_path))
    store.consume_matching_retry_handoff(task, str(locator_path))
    ```
  - After:
    ```python
    record["retry_handoff_id"] = handoff_id
    write_json(locator_path, record)
    store.commit_retry_handoff_locator(task, handoff_id, locator_path)
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: identity validation, one-save commit, rollback, caller threading.
- 테스트 작성: 작성. `ThroughputQuotaBatchTest`에 retry context source locator와 new attempt locator가 다른 crash window에서 handoff id로 정확히 한 번 consume되는 test를 둔다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_handoff_locator_consume_restart_windows -v`.

### [REVIEW_REFACTOR-2] Actual restart regression

- 문제: `test_dispatch.py:9401-9532`는 helper를 직접 호출하고, `:9726-9761`은 fake `invoke`가 production locator state commit을 재현하지 않아 current dispatcher restart를 검증하지 못한다.
- 해결 방법:
  - fake provider process seam만 대체하고 production `invoke()`의 locator/state commit을 실행한다.
  - crash-after-locator, commit save-fault, restart를 각각 검증해 probe=1, invoke=1, pending/context consume, source artifact preservation, normal sibling deep equality, subprocess=0을 assert한다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: direct helper fixture를 actual publication/restart matrix로 교체한다.
- 테스트 작성: 작성. `test_retry_restart_preserves_handoff_and_consumes_pending`가 fake provider guard 아래 deterministic PASS가 되도록 고친다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_restart_preserves_handoff_and_consumes_pending -v`.

## 의존 관계 및 구현 순서

1. StateStore durable commit과 caller handoff-id threading을 구현한다.
2. actual locator publication/restart tests를 production path에 연결한다.
3. focused, selected integration, full suite 순으로 evidence를 갱신한다.

## 수정 파일 요약

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

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_handoff_locator_consume_restart_windows -v` — exactly-once consume PASS, provider subprocess 0회.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_handoff_consume_save_fault_preserves_pending -v` — save-fault pre-state rollback PASS.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_restart_preserves_handoff_and_consumes_pending -v` — probe=1, invoke=1, pending consume PASS.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest DispatcherCanonicalFailoverIntegrationTest BlockerDrainTest -v` — selected dispatcher regressions PASS.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'` — 이번 handoff 변경으로 새 실패 없음; `ReviewControlTest.test_final_channel_contract_is_top_level_and_singular`과 `ThroughputQuotaBatchTest.test_retry_evidence_artifact_identity_variants`만 별도 후보로 기록한다.
- `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`의 구현 에이전트 소유 섹션을 채운다.
