<!-- task=m-agent-task-runtime-target-selector/03+01,02_route_pin_state plan=1 tag=REVIEW_REFACTOR -->

# Plan - Selector decision 영속 상태 구현 후속

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

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

## 배경

첫 구현 루프는 계획된 dispatcher bridge, persisted decision state와 회귀 테스트를 전혀 구현하지 않은 채 초기 review stub을 남겼다. 공식 리뷰에서 정적 `route_agent()`, 기존 `StateStore` schema와 누락된 `RouteDecisionPersistenceTest`가 확인되어 FAIL 판정했다. 이 후속은 기존 subtask 경계를 유지하면서 누락된 구현·테스트·검증 evidence를 한 번에 닫는다.

## Archive Evidence Snapshot

- `agent-task/m-agent-task-runtime-target-selector/03+01,02_route_pin_state/code_review_cloud_G07_0.log`: FAIL. Required 4건, Suggested 0건, Nit 0건. selector bridge, decision persistence, 집중 회귀와 implementation evidence가 모두 누락됐다.
- `agent-task/m-agent-task-runtime-target-selector/03+01,02_route_pin_state/plan_local_G07_0.log`: 첫 계획 원문. 변경 대상은 `dispatch.py`와 `test_dispatch.py`, 범위는 route-pin 저장·복원 기반으로 고정돼 있다.
- 실제 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py RouteDecisionPersistenceTest -v`는 `AttributeError: module '__main__' has no attribute 'RouteDecisionPersistenceTest'`로 exit 1이었다.
- 선행 근거는 `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/01_selector_contract/complete.log`와 `agent-task/archive/2026/07/m-agent-task-runtime-target-selector/02+01_quota_input/complete.log`에서 이미 PASS했다. 추가 세부가 필요할 때도 이 두 `complete.log`와 위 현재-loop log만 좁게 읽고 archive를 탐색하지 않는다.
- Roadmap carryover: 이 foundation subtask는 `route-pin` 완료를 단독 주장하지 않으므로 `Roadmap Targets`를 두지 않는다. 후속 `05+04_dispatch_integration`이 실제 invocation까지 연결해 S05를 닫는다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/common/philosophy.md`
- `agent-test/local/rules.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-spec/index.md`
- `agent-contract/index.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-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/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`

### SDD 기준

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md`, 상태 `[승인됨]`, 잠금 해제, 사용자 리뷰 없음.
- 대상은 Acceptance Scenario S05와 Milestone Task `route-pin`이다. Evidence Map S05는 persisted route resume/restart 통합 테스트와 decision state evidence를 요구한다.
- 구현 체크리스트는 같은 `task/plan/tag` 세대의 initial/resume 고정, body-only edit 유지, 새 plan/tag reset, restart와 malformed task-local blocker를 직접 검증하도록 역산했다.
- 이 subtask는 S05의 저장·복원 기반만 구현하므로 PASS 시 Roadmap Task를 체크하지 않는다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`를 끝까지 읽었다.
- agent-ops Python dispatcher에 매칭되는 local profile은 없다. 현재 checkout의 Python `unittest`, `py_compile`, `git diff --check`를 fallback 검증으로 사용한다.
- 외부 provider, Docker, 원격 runner를 사용하지 않으며 test-rule 유지보수는 필요하지 않다.

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

- selector 자체 resume pin과 nested prior validation은 기존 selector 테스트가 다룬다.
- dispatcher에는 selector bridge, canonical `AgentSpec` 변환, stage별 decision/history 저장·복원 테스트가 없다.
- body-only edit, plan/tag 세대 reset, malformed/no-eligible task-local blocker와 독립 branch drain의 결합 회귀가 없다.
- 계획된 `RouteDecisionPersistenceTest` 클래스가 없어 집중 검증이 exit 1로 실패한다.

### 심볼 참조

- rename/remove 없음.
- 새 bridge 소비자는 `route_agent()` 호출부인 `run_worker`, `run_selfcheck`와 persisted state를 읽는 dispatcher scheduling 경로다. 공식 review의 고정 Codex 경계는 유지한다.

### 분할 판단

- split 정책을 재평가했고 기존 `03+01,02_route_pin_state` 경계를 유지한다. selector/policy foundation과 quota input은 predecessor `01`, `02`의 PASS `complete.log`로 충족됐다.
- 이 후속은 첫 루프의 미구현을 그대로 완성하는 단일 ownership slice다. bridge와 state를 다시 분리하면 동일 decision schema와 같은 집중 회귀를 두 계획에서 중복 조정하므로 추가 split이 더 위험하다.
- 후속 failover, broad invocation, selfcheck, throughput과 audit closure는 각각 `04+03`, `05+04`, `06+05`, `07+06`, `08+06,07`에 남긴다.

### 범위 결정 근거

- `select_execution_target.py`와 `execution_target_policy.py`의 공개 selector 계약은 소비만 하며 변경하지 않는다.
- failover target 선택, 실제 adapter invocation 전환, failure-budget 정책 변경, selfcheck/throughput/skill 문서 갱신은 후속 subtask 범위다.
- Node quota producer, agent-spec과 agent-contract는 매칭 활성 문서가 없어 변경하지 않는다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`; 이전 lane/G, 점수와 filename은 입력에서 제외했다.
- finalizer=`finalize-task-policy.sh`, finalizer_mode=`pair`.
- build closures: `scope_closed=true`(Required 4건과 S05 기반 범위 확정), `context_closed=true`(dispatcher/selector/test 전체를 한 slice에서 유지), `verification_closed=true`(집중·전체 unittest와 compile/diff 명령 확정), `evidence_trusted=true`(누락 테스트 exit 1을 직접 재현), `ownership_closed=true`(dispatcher 내부 상태만 변경), `decision_closed=true`(Milestone 잠금 해제·결정 필요 없음).
- review closures: 위 source/test와 실제 명령 출력으로 correctness, persistence, task-local isolation을 독립 판정할 수 있어 모두 `true`다.
- build route_basis=`local-fit`; capability_gap=`none`; grade scores `scope_coupling/state_concurrency/blast_irreversibility/evidence_diagnosis/verification_complexity=2/2/1/1/1`, final=`local-G07`, filename=`PLAN-local-G07.md`.
- review route_basis=`official-review`; capability_gap=`none`; grade scores `2/2/1/1/1`, final=`cloud-G07`, filename=`CODE_REVIEW-cloud-G07.md`; target=`codex/gpt-5.6-sol xhigh`.
- build loop-risk: ordered state는 initial/resume/block과 restart/stale/malformed 경로를 가지며 `temporal_state`; dispatcher-selector-policy 3개 component와 worker/review consumer validation으로 `boundary_contract`; stage/generation/error variant 조합으로 `variant_product`; nested JSON과 target namespace 변환으로 `structured_interpretation`이 매칭됐다. signature는 route나 G를 변경하지 않았다.
- floor 적용 없음.

## 구현 체크리스트

- [ ] dispatcher가 selector를 단일 내부 bridge로 호출하고 JSON decision을 검증된 canonical `AgentSpec`으로 변환해 worker/review decision 경로에 연결한다.
- [ ] `StateStore`가 stage별 selector decision과 transition history를 `task/plan/tag` 세대에 보존하고 malformed/stale/no-eligible state를 해당 task blocker로 fail-closed 처리한다.
- [ ] reopen, 시간 경계, PLAN 본문 변경, 새 plan/tag 세대, malformed/no-eligible state와 독립 branch drain 회귀를 추가하고 집중·전체 검증을 통과한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REFACTOR-1] Selector bridge와 canonical AgentSpec

- 문제: `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:977`의 `route_agent()`가 정적 matrix를 다시 구현하고 selector JSON을 소비하지 않는다.
- 해결 방법:

```python
# Before: dispatch.py:977
def route_agent(task: Task) -> AgentSpec:
    if task.lane is None or task.grade is None:
        raise RuntimeError("PLAN route를 판별할 수 없다")
```

```python
# After
def evaluate_execution_decision(task, *, stage, transition, prior, quota, evaluated_at):
    ...

def agent_spec_from_decision(decision):
    ...
```

  sibling selector를 path-stable하게 load하고 selector error를 typed task-local blocker로 정규화한다. persisted decision에는 canonical target을 보존하고 Pi의 검증된 `iop/` prefix만 CLI model 변환에서 제거한다. `execution_class`, `selfcheck_required`와 review 고정 target은 손실 없이 유지한다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: selector loader/evaluator, decision validator와 `AgentSpec` 변환, worker/review decision bridge.
- 테스트 작성: `test_dispatch.py::RouteDecisionPersistenceTest`에 worker/review canonical target, Pi prefix 변환, malformed/no-eligible task-local blocker와 독립 ready branch를 추가한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py RouteDecisionPersistenceTest -v`가 PASS해야 한다.

### [REVIEW_REFACTOR-2] 세대별 persisted decision 상태

- 문제: `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:473`의 기본/peek state는 worker 결과와 failure count만 저장하고 selector decision/history를 보존하지 않는다.
- 해결 방법:

```python
# Before: dispatch.py:477
current = {
    "plan_hash": task.plan_hash,
    "worker_done": False,
    "recovery_failures": {},
}
```

```python
# After
current = {
    ...,
    "execution_decisions": {},
    "route_transition_history": [],
}
```

  `plan_identity()`가 제공하는 `task/plan/tag` 세대를 유지해 body-only 변경은 같은 state를 쓰고 plan/tag 변경만 새 state를 만든다. 저장된 nested decision은 selector validator로 재검증하며 손상·stale identity를 자동 재선택하지 않고 해당 task만 차단한다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: state default/peek/update와 stage별 decision 저장·복원/history helper.
- 테스트 작성: 실제 temp `state.json`을 reopen해 day/night resume, body-only edit, plan/tag generation reset, corrupt nested decision을 검증한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py RouteDecisionPersistenceTest -v`가 PASS해야 한다.

### [REVIEW_REFACTOR-3] Decision state 회귀와 evidence 완결

- 문제: `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py:4057` 이후에도 계획된 test class가 없고 집중 명령은 클래스 누락으로 exit 1이다.
- 해결 방법: temp workspace와 실제 `StateStore` reopen을 사용하고 selector clock/quota를 주입해 byte-stable decision, reset 경계와 branch isolation을 한 class에서 검증한다. 구현 후 review stub의 설계 결정, deviation과 각 명령 원문 출력을 채운다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: `RouteDecisionPersistenceTest`와 필요한 fixture.
  - [ ] `agent-task/m-agent-task-runtime-target-selector/03+01,02_route_pin_state/CODE_REVIEW-cloud-G07.md`: implementation-owned checklist/evidence.
- 테스트 작성: reopen pin, KST 경계, body-only 유지, 새 plan/tag reset, malformed/no-eligible blocker와 독립 branch 지속을 모두 추가한다.
- 중간 검증: `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*execution_target*.py'`가 PASS해야 한다.

## 의존 관계 및 구현 순서

1. predecessor `01_selector_contract`와 `02+01_quota_input`은 위 PASS `complete.log`로 충족됐다.
2. REVIEW_REFACTOR-1 → REVIEW_REFACTOR-2 → REVIEW_REFACTOR-3 순서로 구현한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` | REVIEW_REFACTOR-1, REVIEW_REFACTOR-2 |
| `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` | REVIEW_REFACTOR-1, REVIEW_REFACTOR-2, REVIEW_REFACTOR-3 |
| `agent-task/m-agent-task-runtime-target-selector/03+01,02_route_pin_state/CODE_REVIEW-cloud-G07.md` | REVIEW_REFACTOR-3 evidence |

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py RouteDecisionPersistenceTest -v` — 새 state 회귀 PASS.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*execution_target*.py'` — selector/policy 집중 suite PASS.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'` — 전체 suite fresh process 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` — 출력 없음.

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