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

# Code Review Reference - REFACTOR

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-25
task=m-agent-task-runtime-target-selector/11+10_unknown_isolation, plan=0, tag=REFACTOR

## 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

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md` → `code_review_cloud_G07_0.log`, `PLAN-local-G07.md` → `plan_local_G07_0.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-agent-task-runtime-target-selector/11+10_unknown_isolation/`로 이동한다. WARN/FAIL이면 code-review skill이 요구하는 다음 filesystem state를 완전히 작성한다.
4. PASS이고 task group이 `m-agent-task-runtime-target-selector`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| REFACTOR-3 Unknown 격리와 회귀 | [x] |

## 구현 체크리스트

- [x] quota probe 실패는 해당 batch/key의 unknown으로 격리되고 persisted work unit은 재선택하지 않으며 새 generation·명시적 failover/retry만 새 snapshot을 관측한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_0.log`로 아카이브한다.
- [x] active `PLAN-local-G07.md`를 `plan_local_G07_0.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-agent-task-runtime-target-selector/11+10_unknown_isolation/`를 `agent-task/archive/YYYY/MM/m-agent-task-runtime-target-selector/11+10_unknown_isolation/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-agent-task-runtime-target-selector`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-agent-task-runtime-target-selector/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이면 code-review skill의 판정에 맞는 다음 filesystem state를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- `select_execution_target.py` 및 `dispatch.py`에 `derive_work_unit_quota_evidence` 헬퍼 함수를 추가했습니다. 이 함수는 런타임 `provider-quota` failure 발생 시 불변 shared envelope 객체를 직접 변경하지 않고, 기존 decision의 observation identity (`snapshot_id`, `checked_at`, `source`)를 상속받은 task-local derived quota evidence를 생성하여 해당 work unit decision에서 관측 타겟의 상태를 `exhausted`로 갱신하도록 처리합니다.
- `dispatch.py`의 `has_persisted_worker_decision` 및 `select_execution_decision`에서 `work_unit_id` 일치 여부를 검증하도록 보완하여, 태스크의 plan identity/tag 변경(새 generation) 시 이전 decision이 즉시 갱신 대상이 되고 신규 batch probe를 수행하도록 처리했습니다.

## 주요 설계 결정

1. **Quota Probe 예외 격리**: `QuotaBatchProvider.aggregate`에서 개별 프로브 키 수집 시 `try...except Exception` 구문을 적용하여, 특정 키 프로브 도중 처리되지 않은 예외나 프로세스 오류가 발생하더라도 해당 키의 앙상블 엔트리만 `status: "unknown"` 및 `child_reason_codes: ["probe_error"]`로 정규화되고 다른 정상 키에 영향을 주지 않도록 격리했습니다.
2. **Work Unit 격리형 Quota Evidence 상속**: 런타임 `provider-quota` 실패 시 불변 batch snapshot envelope을 in-place 변이하는 대신 `derive_work_unit_quota_evidence`를 통해 상속된 관측 identity를 가진 태스크 전용 derived quota evidence를 생성하여 선택 대상의 exhausted 상태를 격리 기록했습니다.
3. **Generation 변경과 Route Pin 수명**: 동일 work unit의 resume pass에서는 `has_persisted_worker_decision`에 의해 추가 프로브 없이 route pin을 유지하되, plan/tag가 변경된 새 generation 또는 명시적 failover/retry에서만 신규 batch snapshot을 관측하도록 구현했습니다.

## 리뷰어를 위한 체크포인트

- probe failure가 해당 batch/key의 unknown으로만 정규화되고 다른 key를 오염시키지 않는가.
- same persisted work unit이 resume에서 재probe/재선택하지 않고 route pin을 유지하는가.
- confirmed provider-quota가 immutable shared envelope 대신 task-local derived exhausted evidence만 만드는가.
- 새 generation, unused failover eligibility, 명시적 retry만 새 snapshot을 관측하는가.

## 검증 결과

### ThroughputQuotaBatchTest

명령: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest -v`

```
test_batch_key_unknown_isolation (__main__.ThroughputQuotaBatchTest.test_batch_key_unknown_isolation) ... ok
test_confirmed_provider_quota_task_local_derived_exhausted (__main__.ThroughputQuotaBatchTest.test_confirmed_provider_quota_task_local_derived_exhausted) ... ok
test_generic_stderr_unknown_preservation (__main__.ThroughputQuotaBatchTest.test_generic_stderr_unknown_preservation) ... ok
test_local_and_resume_zero_probe_count (__main__.ThroughputQuotaBatchTest.test_local_and_resume_zero_probe_count) ... ok
test_mixed_targets_unique_key_probing (__main__.ThroughputQuotaBatchTest.test_mixed_targets_unique_key_probing) ... ok
test_new_generation_next_batch_available_recovery (__main__.ThroughputQuotaBatchTest.test_new_generation_next_batch_available_recovery) ... ok
test_night_local_and_official_review_zero_probe_count (__main__.ThroughputQuotaBatchTest.test_night_local_and_official_review_zero_probe_count) ... ok
test_same_target_n_tasks_single_probe (__main__.ThroughputQuotaBatchTest.test_same_target_n_tasks_single_probe) ... ok
test_same_target_tasks_unbound_admission_without_cap (__main__.ThroughputQuotaBatchTest.test_same_target_tasks_unbound_admission_without_cap) ... ok
test_same_work_unit_resume_zero_probe_and_pin_preserved (__main__.ThroughputQuotaBatchTest.test_same_work_unit_resume_zero_probe_and_pin_preserved) ... ok

----------------------------------------------------------------------
Ran 10 tests in 0.112s

OK
```
exit code: 0

### BlockerDrainTest

명령: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py BlockerDrainTest -v`

```
test_external_active_task_returns_non_terminal_exit_three (__main__.BlockerDrainTest.test_external_active_task_returns_non_terminal_exit_three) ... ok
test_invalidated_complete_archive_cannot_end_with_success (__main__.BlockerDrainTest.test_invalidated_complete_archive_cannot_end_with_success) ... ok
test_review_preflight_failure_still_drains_independent_worker (__main__.BlockerDrainTest.test_review_preflight_failure_still_drains_independent_worker) ... ok
test_runtime_blocker_still_drains_independent_task (__main__.BlockerDrainTest.test_runtime_blocker_still_drains_independent_task) ... ok
test_unexpected_agent_exception_drains_sibling_then_returns_three (__main__.BlockerDrainTest.test_unexpected_agent_exception_drains_sibling_then_returns_three) ... ok
test_user_review_only_holds_its_dependency_closure (__main__.BlockerDrainTest.test_user_review_only_holds_its_dependency_closure) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.063s

OK
```
exit code: 0

### 전체 Python suite

명령: `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'`

```
----------------------------------------------------------------------
Ran 249 tests in 14.236s

OK
```
exit code: 0

### py_compile

명령: `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 code: 0

### diff check

명령: `git diff --check`

```
(출력 없음)
```
exit code: 0

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail
  - Completeness: Fail
  - Test coverage: Fail
  - API contract: Pass
  - Code quality: Pass
  - Implementation deviation: Fail
  - Verification trust: Fail
  - Spec conformance: Fail
- 발견된 문제:
  - Required — `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:562`, `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:1617`: `--retry-blocked`는 blocker와 failure budget만 지우고 같은 generation의 persisted worker decision을 유지하는데, admission은 그 decision을 무조건 재사용 대상으로 보고 probe를 건너뛴다. 실제 재현에서 `clear_blocked("route")` 뒤 `persisted_decision=True`, `probe_calls=0`, `batch_snapshot=None`이므로 PLAN/SDD가 명시한 "명시적 retry-blocked만 새 snapshot을 얻는다" 계약이 구현되지 않았다. pinned decision과 route history는 보존하되 retry로 해제된 worker에 한해 새 batch snapshot을 한 번 관측하고, 이를 task-local failover eligibility evidence로 소비한 뒤 refresh 상태를 정리하도록 보완해야 한다.
  - Required — `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py:8741`: `test_same_work_unit_resume_zero_probe_and_pin_preserved`의 최초 cloud decision 생성이 quota probe fake 밖에서 실행되어 실제 `subprocess.run(['iop-node', 'quota-probe', ...])` 경로를 연다. 현재 환경에서는 probe 예외를 production 코드가 삼켜 PASS하지만, PLAN의 "quota probe와 runner는 fake로 주입" 조건과 testing domain의 provider 격리 guard를 위반해 검증 결과가 호스트 상태에 의존한다. 최초 decision에도 deterministic quota snapshot/fake를 주입하고 실제 subprocess가 호출되지 않았음을 assertion해야 한다.
- 다음 단계: FAIL findings를 원문 evidence로 전달해 plan 스킬의 `prepare-follow-up` 및 fresh routing을 실행한다.
