<!-- task=m-agent-task-runtime-target-selector/08+07_selfcheck_policy plan=6 tag=REVIEW_TEST -->

# Code Review Reference - REVIEW_TEST

> **[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-26
task=m-agent-task-runtime-target-selector/08+07_selfcheck_policy, plan=6, tag=REVIEW_TEST

## 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:
  - `selfcheck-policy`: 실제 worker 완료 target의 local/cloud 성격으로 selfcheck 결정
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Task: `agent-task/m-agent-task-runtime-target-selector/08+07_selfcheck_policy/`
- Prior loop artifacts: `agent-task/m-agent-task-runtime-target-selector/08+07_selfcheck_policy/plan_local_G03_5.log`, `agent-task/m-agent-task-runtime-target-selector/08+07_selfcheck_policy/code_review_cloud_G03_5.log`
- Verdict: `FAIL`
- Issue summary: Required 1, Suggested 0, Nit 0 — valid cloud 행렬은 통과하지만 invalid commit/restart 경로가 canonical `_CLOUD_CASES`와 decision factory를 재사용하지 않아 계획의 단일 source of truth 기준을 충족하지 못했다.
- Affected files: `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- Verification evidence: focused 24건, pinned-target integration 1건, 전체 238건, `py_compile`, `git diff --check`는 모두 fresh process에서 PASS했다.
- Roadmap carryover: Milestone Task `selfcheck-policy`, SDD Acceptance Scenario S10과 Evidence Map S10을 계속 대상으로 한다.
- Narrow reread allowed: 정확한 직전 판정 문맥이 필요할 때 위 `plan_local_G03_5.log`와 `code_review_cloud_G03_5.log`만 읽는다.

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

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

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

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

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| REVIEW_TEST-1 Canonical cloud fixture 단일화 | [x] |

## 구현 체크리스트

- [x] `CompletingTargetSelfcheckTest.make_cloud_decision()`이 `execution_class` variant를 만들 수 있게 하고 validation, valid/invalid `_mark_worker_done()`, valid/invalid restart matrix가 모두 class-level `_CLOUD_CASES`와 이 factory를 소비하도록 바꾸며 inline `cloud_cases =`/중복 decision dict를 제거한다.
- [x] source-shape 검색, focused class, pinned-target integration, 전체 Python suite, `py_compile`, `git diff --check`를 fresh process에서 통과시킨다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

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

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

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

## 계획 대비 변경 사항

계획에 명시된 inline `cloud_cases = self._CLOUD_CASES` 지역 alias 제거 외에 추가로 `test_completing_decision_validation_matrix` 내 `_spec_from_completing_decision` 호출 부의 inline `cloud_cases = [...]` 리터럴 리스트도 제거했다. 계획의 ‘한 번만 정의’ 기준에 따라 동일 class 내 5곳(.factory 1곳 signature 변경 + inline table 4곳)을 단일화했다.

## 주요 설계 결정

1. `make_cloud_decision`에 `execution_class: str = "cloud_model"` 선택적 파라미터를 추가했다. valid/cloud_model 경로는 기존 호출 부를 변경 없이 그대로 사용하고, invalid/local_model 및 malformed/42 경로는 factory에 execution_class 인자를 전달해 단일 source of truth를 유지한다.
2. 기존 `valid_cloud_cases = {adapter: self.make_cloud_decision(adapter, target) for adapter, target in self._CLOUD_CASES}` dict comprehension은 유지했다. 이는 `_CLOUD_CASES`를 소스로 사용하는 derived cache이므로 계획의 단일 source of truth 기준을 위반하지 않는다.
3. 새 test method를 추가하지 않고 기존 24개 focused method의 fixture 생성만 치환했다.

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

- `_CLOUD_CASES`가 class-level 한 곳에만 있고 모든 cloud validation/commit/restart matrix가 이를 직접 순회하는가.
- `make_cloud_decision()`이 valid `cloud_model`과 invalid/non-string execution class variant를 만들며 `selfcheck_required=False` 불변식을 유지하는가.
- `cloud_cases =` 지역 table/alias와 adapter/target literal table이 남아 있지 않은가.
- 세 adapter의 valid commit/restart와 invalid no-commit/blocked assertion, provider-deny guard가 그대로 유지되는가.

## 검증 결과

아래 명령을 실제로 실행하고 각 섹션에 stdout/stderr와 exit code를 기록한다. 요약이나 이전 cached 출력으로 대체하지 않는다.

### canonical fixture references

명령: `rg --sort path -n '_CLOUD_CASES|make_cloud_decision' agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`

exit code: 0

```text
405:    _CLOUD_CASES = (
412:    def make_cloud_decision(
1029:                adapter: self.make_cloud_decision(adapter, target)
1030:                for adapter, target in self._CLOUD_CASES
1088:            for adapter, target in self._CLOUD_CASES:
1117:            for adapter, target in self._CLOUD_CASES:
1119:                    invalid = self.make_cloud_decision(adapter, target, "local_model")
1180:            for adapter, target in self._CLOUD_CASES:
1182:                    invalid_full = self.make_cloud_decision(adapter, target, "local_model")
1353:        for adapter, target in self._CLOUD_CASES:
1361:                        bad_decision = self.make_cloud_decision(adapter, target, "local_model")
1385:        for adapter, target in self._CLOUD_CASES:
1394:                        bad_decision = self.make_cloud_decision(adapter, target, 42)
1421:        for adapter, target in self._CLOUD_CASES:
1429:                        bad_decision = self.make_cloud_decision(adapter, target, "local_model")
1455:        for adapter, target in self._CLOUD_CASES:
1463:                        valid_decision = self.make_cloud_decision(adapter, target)
1510:        for adapter, target in self._CLOUD_CASES:
1518:                        valid_decision = self.make_cloud_decision(adapter, target)
```

### inline cloud table absence

명령: `! rg --sort path -n '^[[:space:]]+cloud_cases[[:space:]]*=' agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`

exit code: 1 (no match — inline `cloud_cases =` 정의가 존재하지 않음)

```text
(일치 항목 없음)
```

### CompletingTargetSelfcheckTest

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

exit code: 0

```text
test_canonical_model_command_generation (__main__.CompletingTargetSelfcheckTest.test_canonical_model_command_generation)
Selfcheck spec.model is normalized (iop/ prefix stripped) for command generation. ... ok
test_cloud_completing_decision_skips_selfcheck (__main__.CompletingTargetSelfcheckTest.test_cloud_completing_decision_skips_selfcheck)
execution_class=cloud_model skips selfcheck entirely. ... ok
test_completing_decision_requires_selfcheck_matrix (__main__.CompletingTargetSelfcheckTest.test_completing_decision_requires_selfcheck_matrix)
Matrix: local_model→True, cloud_model→False, missing→False. ... ok
test_completing_decision_validation_matrix (__main__.CompletingTargetSelfcheckTest.test_completing_decision_validation_matrix)
_completing_decision_is_valid enforces stage, work_unit_id, and schema contract. ... ok
test_identity_mismatch_decision_blocked_at_scheduler (__main__.CompletingTargetSelfcheckTest.test_identity_mismatch_decision_blocked_at_scheduler)
worker_done=True with missing completing decision blocks at scheduler entry. ... ok
test_identity_mismatch_fails_closed (__main__.CompletingTargetSelfcheckTest.test_identity_mismatch_fails_closed)
Missing or malformed completing decision blocks selfcheck. ... ok
test_identity_mismatch_malformed_decision_blocked (__main__.CompletingTargetSelfcheckTest.test_identity_mismatch_malformed_decision_blocked)
worker_done=True with malformed completing decision blocks at scheduler. ... ok
test_local_completing_decision_triggers_selfcheck (__main__.CompletingTargetSelfcheckTest.test_local_completing_decision_triggers_selfcheck)
execution_class=local_model schedules exactly one selfcheck. ... ok
test_mark_worker_done_does_not_fallback_from_malformed_persisted_worker_decision (__main__.CompletingTargetSelfcheckTest.test_mark_worker_done_does_not_fallback_from_malformed_persisted_worker_decision)
Malformed persisted worker decision blocks without falling back to initial. ... ok
test_mark_worker_done_rejects_cloud_decision_with_local_execution_class (__main__.CompletingTargetSelfcheckTest.test_mark_worker_done_rejects_cloud_decision_with_local_execution_class)
_mark_worker_done rejects cloud adapter + local_model + False for every adapter. ... ok
test_mark_worker_done_validates_cloud_decision_commit_matrix (__main__.CompletingTargetSelfcheckTest.test_mark_worker_done_validates_cloud_decision_commit_matrix)
Valid cloud decision commits worker_done=True, selfcheck_done=True, stage=review. ... ok
test_mark_worker_done_validates_cloud_identity (__main__.CompletingTargetSelfcheckTest.test_mark_worker_done_validates_cloud_identity)
_mark_worker_done rejects cloud decision with mismatched worker CLI. ... ok
test_mark_worker_done_validates_pi_identity (__main__.CompletingTargetSelfcheckTest.test_mark_worker_done_validates_pi_identity)
_mark_worker_done rejects Pi decision with mismatched worker model. ... ok
test_restart_does_not_duplicate_selfcheck (__main__.CompletingTargetSelfcheckTest.test_restart_does_not_duplicate_selfcheck)
After restart, already-completed selfcheck is not re-executed. ... ok
test_restart_valid_cloud_advances_to_review (__main__.CompletingTargetSelfcheckTest.test_restart_valid_cloud_advances_to_review)
Restart with valid cloud completing decision goes to review, not selfcheck. ... ok
test_restart_with_cloud_local_mismatch_blocks (__main__.CompletingTargetSelfcheckTest.test_restart_with_cloud_local_mismatch_blocks)
Restart with cloud adapter + local_model + False blocks for every adapter, not selfcheck. ... ok
test_restart_with_malformed_completed_state_blocks_not_selfcheck (__main__.CompletingTargetSelfcheckTest.test_restart_with_malformed_completed_state_blocks_not_selfcheck)
Restart with malformed completing decision blocks, does not enter selfcheck. ... ok
test_run_worker_completion_mismatch_blocks_task_without_raise (__main__.CompletingTargetSelfcheckTest.test_run_worker_completion_mismatch_blocks_task_without_raise)
run_worker converts completion validation failure to task-local blocker. ... ok
test_selector_probe_not_invoked_during_selfcheck (__main__.CompletingTargetSelfcheckTest.test_selector_probe_not_invoked_during_selfcheck)
Selfcheck does not call selector or quota probe. ... ok
test_selfcheck_reuses_completing_target_no_selector_call (__main__.CompletingTargetSelfcheckTest.test_selfcheck_reuses_completing_target_no_selector_call)
Selfcheck uses the completing decision's target, not re-evaluating selector. ... ok
test_spec_from_completing_decision_normalizes_pi_target (__main__.CompletingTargetSelfcheckTest.test_spec_from_completing_decision_normalizes_pi_target)
_spec_from_completing_decision strips iop/ prefix from model. ... ok
test_spec_from_completing_decision_rejects_invalid_pi_target (__main__.CompletingTargetSelfcheckTest.test_spec_from_completing_decision_rejects_invalid_pi_target)
_spec_from_completing_decision rejects Pi target without iop/ prefix. ... ok
test_spec_from_completing_decision_rejects_non_pi_cloud (__main__.CompletingTargetSelfcheckTest.test_spec_from_completing_decision_rejects_non_pi_cloud)
_spec_from_completing_decision rejects cloud target with selfcheck_required=True. ... ok
test_worker_persists_actual_completing_decision_and_execution_class (__main__.CompletingTargetSelfcheckTest.test_worker_persists_actual_completing_decision_and_execution_class)
Worker success records the completing decision, not the initial one. ... ok

----------------------------------------------------------------------
Ran 24 tests in 0.135s

OK
```

### pinned target integration

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

exit code: 0

```text
test_completing_target_controls_selfcheck_and_reuses_pin (__main__.SelectorDispatcherIntegrationTest.test_completing_target_controls_selfcheck_and_reuses_pin) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.115s

OK
```

### 전체 Python suite

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

exit code: 0

```text
Ran 238 tests in 14.862s

OK
```

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

```text
(출력 없음 — syntax error 없음)
```

### diff check

명령: `git diff --check`

exit code: 0

```text
(출력 없음 — whitespace error 없음)
```

---

> **[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 |

## 코드리뷰 결과

- 종합 판정: PASS

### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| Correctness | Pass | 세 cloud adapter의 valid/invalid validation, completion commit, restart 경로가 canonical fixture를 사용하며 기대 상태 전이를 유지한다. |
| Completeness | Pass | 직전 Required의 `_CLOUD_CASES` 및 factory 단일화가 validation, valid/invalid commit, valid/invalid restart matrix 전체에 반영됐다. |
| Test coverage | Pass | focused 24건, pinned-target integration 1건, 전체 238건이 fresh process에서 통과했다. |
| API contract | Pass | production API와 wire 계약 변경 없이 test fixture 구조만 정리했다. |
| Code quality | Pass | inline `cloud_cases =`와 반복 decision dict를 제거했고, 리뷰 중 invalid variant 의도에 맞게 `execution_class` annotation을 `object`로 정렬했다. |
| Implementation deviation | Pass | production dispatcher를 건드리지 않는 follow-up 범위를 지켰고 계획된 단일 source of truth를 충족한다. |
| Verification trust | Pass | source-shape 검색, focused/integration/full suite, `py_compile`, `git diff --check`를 리뷰 세션에서 재현했다. |
| Spec conformance | Pass | SDD S10의 cloud completion selfcheck 생략과 invalid completion 차단 evidence를 보존한다. |

### 발견된 문제

- 없음

### 리뷰 중 직접 보정

- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py:413`: non-string invalid fixture를 의도적으로 허용하는 helper 계약에 맞춰 `execution_class` annotation을 `str`에서 `object`로 정렬했다. 동작 변경은 없다.

### 다음 단계
