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

# Plan - live locator scheduler 재시작 회귀 보강

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

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

## 배경

현재 retry handoff의 최초 locator ID와 save-fault guard는 production 경로로 검증된다. 그러나 restart 회귀는 scheduler를 우회해 `run_worker()`를 직접 두 번 호출하고 두 번째 provider seam 도달을 정상으로 취급한다. StateStore 재개방 뒤 live locator를 scheduler가 감지하면 새 worker/provider launch가 없어야 한다는 exactly-once 경계를 실제 dispatcher 경로로 고정한다.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G08_13.log`
- Prior review: `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_13.log`
- Verdict: `FAIL`
- Required: restart 회귀가 scheduler live-locator gate를 우회해 provider seam 2회를 허용하므로 추가 launch 0회를 증명하지 못한다.
- Suggested: 없음
- Nit: 없음
- Affected files: `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- Verification evidence: focused 3개와 selected dispatcher 17개는 PASS했다. `py_compile`과 `git diff --check`는 PASS했다.
- Ownership/dependency: 기존 split subtask `11+10_unknown_isolation`과 testing domain 소유이며, 신규 predecessor·사용자 결정·외부 권한은 없다.
- Roadmap carryover: `throughput-policy`, SDD S06/S09/S11의 persisted continuation, dispatch integration, sibling 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-G08.md`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/CODE_REVIEW-cloud-G07.md`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/plan_local_G07_12.log`
- `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/code_review_cloud_G07_12.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 잠금과 Milestone 구현 잠금은 `해제`다.
- S06 `context-failover`는 stable logical continuation을, S09 `dispatch-integration`은 persisted decision과 dispatcher 재시작 invocation 일치를, S11 `throughput-policy`는 independent sibling quota snapshot 불변을 요구한다.
- Evidence Map S06/S09/S11에 따라 scheduler의 live locator 분류, 새 provider launch 0회, reopened state와 sibling deep equality를 같은 checklist와 최종 검증으로 묶는다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 `agent-test/local/testing-smoke.md`를 읽었다.
- dispatcher unit/integration simulation은 실제 `pi`, `agy`, `claude`, `codex` provider process와 네트워크를 시작하지 않는다. live evidence는 test-owned deterministic Python child 또는 liveness seam으로만 만들고 provider seam은 deny guard로 막는다.
- 이 Python dispatcher 상태 전이는 Edge-Node dev script, mock smoke, live provider preflight 범위가 아니므로 해당 profile 명령은 의도적으로 사용하지 않는다.
- 검증 시작 Go identity: `/config/.local/bin/go` -> `/config/opt/go/bin/go`, `go1.26.2`, `GOROOT=/config/opt/go`; Python 검증은 Go cache를 쓰지 않는다.

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

- `test_retry_handoff_first_locator_record_and_commit_guard`와 `test_retry_handoff_production_save_fault_preserves_pending`은 first-record ID와 commit save-fault guard를 production path로 덮는다.
- `test_retry_restart_does_not_duplicate_provider_or_mutate_sibling`은 `run_worker()` 직접 호출과 provider seam 2회 기대 때문에 scheduler live-locator restart 경계를 덮지 못한다.

### 심볼 참조

- renamed/removed symbol: 없음.
- 관련 호출: `dispatch_with_store()`의 scheduler live gate, `external_active_is_live()`, `run_worker()`, `StateStore.commit_retry_handoff_locator()`.

### 분할 판단

- production 변경과 test-only closure를 분리할 API·도메인·독립 완료 경계가 없다. scheduler liveness, reopened handoff state, sibling snapshot은 하나의 deterministic regression 안에서만 판정 가능하므로 기존 단일 split subtask를 유지한다.

### 범위 결정 근거

- `dispatch.py`의 live-locator 구현, selector 정책, quota schema, lane/G finalizer, Edge/Node 코드, `WORK_LOG.md`, `SKILL.md`의 scope 밖 final-channel 계약 실패, unrelated dirty files는 수정하지 않는다.
- 이 follow-up은 `test_dispatch.py`의 restart regression만 scheduler 수준의 acceptance evidence로 교체한다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`, `finalizer=finalize-task-policy.sh`, `finalizer_mode=pair`.
- build/review closures는 scope, context, verification, evidence trust, ownership, decision 모두 true다. source liveness gate와 exact failing test가 범위를 닫고, deterministic local tests로 판정 가능하며 외부 권한·사용자 결정은 없다.
- build `route_basis=local-fit`, `capability_gap=none`, scores=`1/2/1/1/1`; loop-risk는 scheduler/restart/active/consumed 상태, dispatcher와 StateStore actor, locator/state/scheduler/provider-seam component, live/stale·reopen·sibling variant를 근거로 `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`.
- finalizer output: build=`PLAN-local-G06.md`, review=`CODE_REVIEW-cloud-G06.md`, official target=`codex/gpt-5.6-sol xhigh`.

## 구현 체크리스트

- [ ] StateStore close/reopen 뒤 `dispatch_with_store()`가 active worker의 live locator를 external-active로 분류해 `run_worker()`와 provider subprocess seam을 다시 호출하지 않음을 deterministic regression으로 검증한다.
- [ ] retry handoff가 이미 한 번 consume된 상태와 independent normal sibling의 quota/decision/transition history deep equality를 restart 전후로 검증하고, first-record·save-fault regression을 유지한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REFACTOR-1] scheduler live locator restart regression

- 문제: `test_dispatch.py:9845-9995`는 production scheduler를 우회해 `run_worker()`를 두 번 호출하고 provider seam 2회를 기대한다. `dispatch.py:5558-5582`의 `active_stage` + `external_active_is_live()` gate가 restart에서 새 worker를 막는지를 증명하지 못한다.
- 해결 방법:
  - 첫 handoff commit 뒤 StateStore를 close/reopen하고 `active_stage=worker`와 running locator의 live PID/start-token 또는 process-marker evidence를 유지한다.
  - reopened store에 대해 실제 `dispatch_with_store()`를 실행하고 `run_worker()` 및 `asyncio.create_subprocess_exec` deny guard가 호출되지 않으며 non-terminal external-active 결과가 나는지 확인한다.
  - same task의 consumed handoff 상태와 independent sibling의 quota snapshot, decision, transition history를 restart 전후 deep-equal로 확인한다.
  - Before:
    ```python
    await dispatch.run_worker(workspace, store2, t_task)
    self.assertEqual(subprocess_count[0], 2)
    ```
  - After:
    ```python
    result = await dispatch.dispatch_with_store(args, workspace, store2)
    self.assertEqual(result, 3)
    run_worker_spy.assert_not_called()
    provider_seam.assert_not_called()
    ```
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: `test_retry_restart_does_not_duplicate_provider_or_mutate_sibling`을 scheduler live-locator restart regression으로 교체한다.
- 테스트 작성: 작성. test-owned live process evidence와 provider deny guard를 사용해 restart 추가 launch 0회, handoff consume, sibling deep equality를 assert한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_restart_does_not_duplicate_provider_or_mutate_sibling -v` — PASS.

## 수정 파일 요약

| 파일 | 변경 항목 |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` | `test_retry_restart_does_not_duplicate_provider_or_mutate_sibling`을 scheduler live-locator restart regression으로 교체 |

### 변경 상세

**기존 테스트 문제점:**
- `dispatch.run_worker()`를 직접 호출하여 scheduler live gate를 우회
- provider seam 2회 도달을 정상으로 취급 (restart 회귀를 증명하지 못함)

**새 테스트 설계:**
1. 먼저 committed handoff와 live agent PID가 기록된 active locator를 pre-populate
2. StateStore close/reopen 시뮬레이션
3. `process_is_alive`를 mock하여 recorded agent PID가 alive하도록 설정
4. `dispatch.dispatch_with_store()` 실행
5. `dispatch.invoke()`가 active task에 대해 호출되지 않았음을 검증
6. provider seam이 도달되지 않았음을 검증
7. dispatch return value가 3 (blocked by externally active task)임을 검증

**검증 포인트:**
- scheduler live-locator gate (`external_active_is_live()`) 가 restart 후 active task를 "외부 실행중"으로 분류
- `dispatch.invoke()` spy 가 active task name 으로 호출되지 않음 (invoke_calls 필터링)
- `asyncio.create_subprocess_exec` spy 가 호출되지 않음 (subprocess_count == 0)
- dispatch return value == 3 ("새 실행 후보 없음, active task는 caller가 계속 추적")

## 검증 출력

```
$ python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest.test_retry_restart_does_not_duplicate_provider_or_mutate_sibling -v

test_retry_restart_does_not_duplicate_provider_or_mutate_sibling (__main__.ThroughputQuotaBatchTest.test_retry_restart_does_not_duplicate_provider_or_mutate_sibling)
Verify scheduler live-locator gate blocks re-launch after StateStore restart. ... ------------------------------------------
작업중: 01_restart
------------------------------------------
task=route/01_restart
stage=worker
route=local-G08
dependency=외부 실행중: stage=worker; agent_pid=99999 alive; output stream is monitored
------------------------------------------
디스패치추적대기: agent-task
------------------------------------------
새 실행 후보 없음
active task는 caller가 계속 추적
ok

Ran 1 test in 0.031s
OK
```

**Focused verification (3개):**
- `test_retry_restart_does_not_duplicate_provider_or_mutate_sibling` — PASS (scheduler live-locator gate restart regression)
- `py_compile agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` — PASS
- `git diff --check agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` — PASS

**Selected dispatcher tests (17개):**
- `ThroughputQuotaBatchTest` 전체 suite — 17 tests PASS (0.387s)

## 구현 에이전트 소유 검증 기록

- **구현 내용:** `test_dispatch.py`의 `test_retry_restart_does_not_duplicate_provider_or_mutate_sibling`을 scheduler live-locator restart regression으로 교체
- **변경 방식:**
  - Before: `dispatch.run_worker()` 직접 호출 + provider seam 2회 기대
  - After: `dispatch.dispatch_with_store()` scheduler 경로 + `process_is_alive` mock으로 live process 시뮬레이션 + `invoke()` spy로 재호출 방지 검증
- **검증 출력:**
  ```
  작업중: 01_restart
  dependency=외부 실행중: stage=worker; agent_pid=99999 alive; output stream is monitored
  디스패치추적대기: agent-task
  새 실행 후보 없음
  active task는 caller가 계속 추적
  ok
  
  Ran 1 test in 0.031s
  OK
  ```
- **ThroughputQuotaBatchTest suite:** 17 tests PASS (0.387s)
- **차단 사항:** 없음
- **재개 조건:** 없음 (완료)
