<!-- task=m-agent-task-runtime-target-selector/04+03_failover_budget plan=8 tag=REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API -->

# Plan - Failover audit·native provenance·actual budget evidence 재수정

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

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

## 배경

여덟 번째 리뷰에서 focused selector 3개, context/budget 3개, 전체 179개 Python suite, `py_compile`, `git diff --check`는 통과했지만 세 계약 결함을 포착하지 못함을 확인했다. 별도 재현에서는 non-quota failover가 prior `available` audit를 `unknown`으로 덮어썼고, locator attempt 밖 native file이 same-Pi resume로 승인됐으며, Claude primary 뒤 Codex alternate failure의 budget audit가 여전히 최초 Claude target과 `initial` transition을 기록했다. active CODE_REVIEW의 구현 소유 evidence도 전부 비어 있다. live dynamic selector invocation은 후속 `05+04_dispatch_integration` 범위로 유지한다.

## Archive Evidence Snapshot

- Task: `agent-task/m-agent-task-runtime-target-selector/04+03_failover_budget`
- Archived plan: `agent-task/m-agent-task-runtime-target-selector/04+03_failover_budget/plan_local_G08_7.log`
- Archived review: `agent-task/m-agent-task-runtime-target-selector/04+03_failover_budget/code_review_cloud_G08_7.log`
- Verdict: `FAIL`
- Findings: Required 4, Suggested 0, Nit 0
- Summary: non-quota audit 보존, same-Pi native session provenance, actual invocation target/transition budget audit, 구현 evidence가 완료되지 않았다.
- Affected files: `scripts/select_execution_target.py`, `scripts/dispatch.py`, `tests/test_select_execution_target.py`, `tests/test_dispatch.py`
- Verification evidence: 리뷰어가 selector focused 3 tests, context/budget focused 3 tests, 전체 Python suite 179 tests, `py_compile`, `git diff --check`를 실행해 모두 PASS를 확인했다. 별도 재현은 `available→unknown`, 외부 native file 승인, actual Codex invocation에도 `last_target=claude`와 `last_transition=initial`이 남는 결함을 확인했다.
- Roadmap carryover: S06 `context-failover`, S07 `failure-budget`; 이 subtask는 기반을 닫고 Milestone Task 완료 체크는 `05+04_dispatch_integration`이 소유한다.

## 분석 결과

### 읽은 파일과 계약 근거

- `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-ops/skills/project/orchestrate-agent-task-loop/SKILL.md`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- `agent-test/local/rules.md`
- `agent-ops/rules/common/rules-agent-spec.md`, `agent-spec/index.md`
- `agent-contract/index.md`
- 승인 SDD는 잠금 해제 상태이고 사용자 결정 대기는 없다. S06은 ordered unused candidate와 no-bounce 및 PLAN/locator/normalized/raw/workspace context evidence를, S07은 같은 work unit/stage의 target 전환·restart 공유, stage 분리, 성공 reset, 10번째 실패 blocker를 요구한다.
- 매칭되는 agent-spec/agent-contract 문서는 없다. 이 follow-up은 S06/S07 기반만 닫고 live S09 integration을 완료하지 않으므로 `Roadmap Targets`는 두지 않는다.

### 테스트 환경과 커버리지 공백

- `test_env=local`; `agent-test/local/rules.md`를 적용한다. 매칭되는 별도 test profile은 없어 repository Python unittest 명령을 사용한다.
- 외부 provider/quota 호출 없이 고정 snapshot, fake subprocess JSON stream, 임시 locator와 persistent `StateStore`를 사용한다.
- `select_execution_target.py:601-609`는 non-quota qualified failure에서도 snapshot 없는 cloud audit를 다시 계산한다.
- `dispatch.py:2864-2868`은 same-Pi native session의 파일 존재만 검사해 attempt/session identity를 검증하지 않는다.
- `dispatch.py:3063-3066`은 실제 `spec` 대신 persisted initial decision의 target/transition을 failure audit에 기록한다.
- `tests/test_select_execution_target.py:993-1032`는 full prior/result audit matrix를 비교하지 않는다.
- `tests/test_dispatch.py:5262-5282`는 session id 없는 `attempt/session.jsonl`을 승인하는 잘못된 동작을 고정한다.
- `tests/test_dispatch.py:5327-5394`는 성공 reset 후 동일 Pi generic failure 10회만 실행해 primary→alternate restart 경계를 검증하지 않는다.
- archived review의 completion table, checklist, 계획 편차, 설계 결정, 검증 출력이 비어 있어 구현 주장을 신뢰할 수 없다.

### 심볼과 범위

- rename/remove와 새 package/import는 없다.
- `_failover()` 결과는 selector CLI와 후속 dispatcher integration이 소비한다.
- `build_context_package()`는 `invoke()`가 만든 locator/raw/normalized/Pi session artifact를 target transition caller에 전달한다.
- `StageFailureBudget.record_failure()`는 `run_escalating()` failure와 성공 reset 경로가 소비하며 budget identity는 `work_unit_id|stage`다.
- 현재 네 source/test와 active review evidence만 수정한다. live selector invocation, completing-target selfcheck, dependency drain/throughput는 `05+04_dispatch_integration`에서 처리한다.
- 새 public API, wire schema, agent-spec, agent-contract, roadmap 변경은 필요하지 않다.

### 분할 판단

- split 정책을 먼저 평가했다. 네 Required는 S06/S07 기반 pair의 provenance, runtime lifecycle, evidence closure이며 동일 네 파일을 함께 사용한다.
- selector/context/budget로 다시 나누면 half-contract와 write-set 충돌이 생기므로 이 subtask의 단일 follow-up으로 유지한다.
- 선행 subtask `03+01,02_route_pin_state`는 `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/03+01,02_route_pin_state/complete.log`에서 PASS 완료가 확인됐다.

### 최종 라우팅

- evaluation_mode=`isolated-reassessment`; 기존 lane/grade를 입력으로 재사용하지 않았다.
- build/review closure의 scope, context, verification, evidence, ownership, decision은 모두 닫혔다.
- build route_basis=`local-fit`, loop-risk=`temporal_state,boundary_contract,structured_interpretation,variant_product`, triggered=`true`.
- build scores(scope_coupling/state_concurrency/blast_irreversibility/evidence_diagnosis/verification_complexity)=`2/2/1/2/1`.
- review route_basis=`official-review`, scores=`2/2/1/2/1`.
- finalizer=`finalize-task-policy.sh`, finalizer_mode=`pair`; build=`local-G08`, `PLAN-local-G08.md`; review=`cloud-G08`, `CODE_REVIEW-cloud-G08.md`; reviewer=`codex/gpt-5.6-sol xhigh`.

## 구현 체크리스트

- [ ] non-quota qualified failover가 모든 prior candidate audit를 그대로 보존하고 provider-quota만 실패 target을 exhausted로 표시하도록 수정·검증한다.
- [ ] same-Pi native session을 locator attempt와 session identity에 묶고 cross-adapter native 비유출을 fail-closed 검증한다.
- [ ] fake JSON `invoke()` artifact의 normalized order와 locator 경로를 확인하고 task/workspace/PLAN/raw/normalized/native 누락·불일치·외부 경로 matrix를 검증한다.
- [ ] 실제 `run_escalating()`과 재open한 `StateStore`로 primary 1회→alternate 9회 budget, actual target/transition audit, stage 분리, 성공 reset, retry-blocked 보존을 검증한다.
- [ ] focused tests, 전체 Python suite, `py_compile`, `git diff --check`를 통과한다.
- [ ] active CODE_REVIEW의 모든 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 구현 항목

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-1] Non-quota audit 보존

- 문제: `_failover()`가 failure class와 무관하게 모든 cloud candidate audit를 snapshot으로 다시 계산한다. snapshot이 없으면 prior `available`이 `unknown`으로 바뀐다.
- 해결 방법: non-quota qualified failure는 prior의 `quota_status`, `eligibility`, `rejection_reason`을 byte-for-byte 보존한다. provider-quota는 failed target만 `exhausted/ineligible/quota_exhausted`로 강제하고, 다른 candidate는 명시적으로 일치하는 snapshot entry가 있을 때만 갱신한다. used candidate/no-bounce는 유지한다.
- 수정 파일:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py`
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py`
- 테스트: provider-quota/non-quota × available/unknown/exhausted의 prior/result full audit tuple과 resume/no-bounce를 table-driven으로 검증한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py SelectorFailoverContractTests -v` — PASS, exit 0.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-2] Same-Pi native provenance

- 문제: raw/normalized는 locator sibling으로 제한되지만 same-Pi native session은 파일 존재만 확인한다.
- 해결 방법: same-Pi에서 non-empty `session_id`, absolute native path, `locator.parent/pi-sessions` 소속, filename/session identity를 검증한다. cross-adapter package에는 native path를 포함하지 않는다. task/workspace/PLAN/raw/normalized exact identity도 유지한다.
- 수정 파일:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- 테스트: fake Pi JSON subprocess를 실제 `invoke()`로 실행해 locator raw/normalized/native와 normalized display order를 확인한다. task/workspace/PLAN/raw/normalized/native의 missing, mismatch, escape, session mismatch와 cross-adapter 비유출을 검증한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest -v` — context/invoke cases PASS, exit 0.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-3] Actual invocation budget audit

- 문제: `run_escalating()`은 같은 stage budget identity를 사용하지만 각 실패의 target/transition을 persisted initial decision에서 읽는다.
- 해결 방법: budget key `work_unit_id|stage`는 유지하고 `record_failure()`에는 현재 invocation `spec`의 canonical target ref와 실제 runtime transition evidence를 전달한다. primary qualified failure 후 alternate로 전환된 상태를 restart 뒤에도 식별할 최소 evidence를 보존한다. live dynamic selector invocation 자체는 `05+04_dispatch_integration`에 남긴다.
- 수정 파일:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- 테스트: primary failure 1회 뒤 `StateStore`를 닫고 재open해 alternate spec 9회로 정확히 10번째에서 block되는지, `last_target`/`last_transition`이 actual alternate인지, review stage는 0인지 확인한다. 성공은 worker budget만 reset하고 `clear_blocked()`는 decision/history/used candidates를 보존해야 한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest -v` — lifecycle cases PASS, exit 0.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-4] 구현 evidence 완결성

- 문제: archived review의 완료 표, 체크리스트, 계획 편차, 설계 결정, 검증 출력이 비어 있다.
- 해결 방법: 위 세 항목의 실제 변경과 명령 출력을 active CODE_REVIEW의 구현 소유 섹션에 동기화한다. 명령을 바꾸면 이유와 대체 명령을 `계획 대비 변경 사항`에 기록한다.
- 수정 파일:
  - [ ] `agent-task/m-agent-task-runtime-target-selector/04+03_failover_budget/CODE_REVIEW-cloud-G08.md`
- 검증: 구현 소유 `[ ]`, placeholder, 빈 verification output이 남지 않았는지 확인한다.

## 의존 관계 및 구현 순서

1. 완료된 predecessor `03+01,02_route_pin_state`의 canonical route pin 계약을 유지한다.
2. API-1 audit contract를 먼저 닫는다.
3. API-2 actual invoke artifact와 native validator를 연결한다.
4. API-3 persistent budget과 actual target transition을 검증한다.
5. API-4로 active CODE_REVIEW evidence를 실제 결과와 동기화한다.
6. 이 task PASS 뒤 `05+04_dispatch_integration`이 live initial/resume/failover invocation을 연결한다.

## 수정 파일 요약

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

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py SelectorFailoverContractTests -v` — audit table/no-bounce PASS, exit 0.
- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py DynamicFailoverBudgetTest -v` — actual invoke provenance와 primary→alternate restart budget PASS, exit 0.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'` — fresh-process 전체 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/scripts/execution_target_policy.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/skills/project/orchestrate-agent-task-loop/tests/test_execution_target_policy.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py` — exit 0.
- `git diff --check` — 출력 없음, exit 0.

Edge-Node user execution smoke/full-cycle은 Python dispatcher runtime contract에 매칭되는 direct user flow가 없어 실행하지 않는다. 모든 코드 변경 완료 후 반드시 active CODE_REVIEW의 구현 에이전트 소유 섹션을 채운다.
