<!-- task=m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission plan=2 tag=REVIEW_PROVIDER_POLICY_EVIDENCE -->

# Provider-pool Queue Policy S06 검증 증거 후속 계획

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

구현과 검증을 완료한 뒤 `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 stdout/stderr로 채우고 active 파일을 그대로 둔 채 리뷰 준비 상태를 보고한다. 종결 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 선택된 Milestone의 `구현 잠금 > 결정 필요`가 구현을 막을 때만 review stub의 `사용자 리뷰 요청`에 정확한 연결 근거를 기록하고 멈춘다. 직접 사용자 질문·선택지·`request_user_input`, `USER_REVIEW.md`, archive log, `complete.log` 작성은 금지한다. 환경/secret/service blocker, 일반 범위 조정, 후속 에이전트가 해소할 수 있는 evidence gap은 사용자 리뷰 요청이 아니다.

## 배경

root provider-pool global cap과 timeout 구현은 focused/race 테스트를 통과하지만, 세 회귀의 fixture가 이름에 적힌 SDD S06 시나리오를 실제로 만들지 않는다. candidate-order 테스트는 단일 후보만 사용하고, legacy-isolation 테스트는 pending legacy item이 없으며, recovery 테스트는 두 waiter 모두 같은 내부 timeout으로 끝난다. 이 후속은 production 코드를 바꾸지 않고 해당 테스트 증거만 truthful하고 deterministic하게 복구한다.

## 사용자 리뷰 요청 흐름

선택된 Milestone lock 결정만 active `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청`에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 사용한다. 구현 중 직접 사용자 prompt는 금지하며 code-review가 요청 검증과 실제 `USER_REVIEW.md` 작성을 소유한다.

## Archive Evidence Snapshot

- Prior task path: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission`
- Prior plan: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/plan_local_G06_1.log`
- Prior review: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/code_review_local_G07_1.log`
- Verdict: FAIL
- Findings: Required 3, Suggested 1, Nit 0.
- Affected files: `apps/edge/internal/service/provider_pool_admission_test.go`.
- Verification evidence:
  - focused bootstrap/service와 race 검증은 PASS했지만 세 테스트 fixture가 candidate-order, legacy pending, cancel/timeout 분리를 실제로 만들지 않았다.
  - 현재 상태의 `go test -count=1 ./apps/edge/...`는 PASS했다.
  - live preflight는 config check PASS 후 외부 `/models`와 Control Plane status endpoint 접근 불가로 rc=3이었다.
- Roadmap carryover: `queue-ownership`은 미완료이며 승인된 SDD S06의 actual two-candidate order, legacy pending 격리, cancel/timeout recovery evidence가 필요하다.
- 구현에 추가 세부가 필요할 때 위 prior plan/review 두 파일만 좁게 다시 읽는다. 다른 `agent-task/archive/**`를 탐색하지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-resource-admission-ownership-alignment.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/operational-observability-provider-management/milestones/provider-resource-admission-ownership-alignment.md)
- Task ids:
  - `queue-ownership`: Edge 공통 provider-pool queue policy와 전체 pending 상한
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- 규칙/스킬: `AGENTS.md`, `agent-ops/rules/project/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/rules/common/rules-agent-spec.md`, `agent-ops/rules/project/domain/edge/rules.md`, `agent-ops/rules/project/domain/platform-common/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`, `agent-ops/skills/common/router.md`, `agent-ops/skills/common/code-review/SKILL.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-ops/skills/common/finalize-task-routing/SKILL.md`
- 로드맵/SDD: `agent-roadmap/current.md`, `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`, `agent-roadmap/phase/operational-observability-provider-management/milestones/provider-resource-admission-ownership-alignment.md`, `agent-roadmap/sdd/operational-observability-provider-management/provider-resource-admission-ownership-alignment/SDD.md`
- 구현 스펙/계약: `agent-spec/index.md`, `agent-spec/runtime/provider-pool-config-refresh.md`, `agent-spec/runtime/edge-node-execution.md`, `agent-contract/index.md`, `agent-contract/inner/edge-config-runtime-refresh.md`, `agent-contract/inner/edge-node-runtime-wire.md`
- 구현: `apps/edge/internal/bootstrap/runtime.go`, `apps/edge/internal/service/service.go`, `apps/edge/internal/service/model_queue_types.go`, `apps/edge/internal/service/model_queue_admission.go`, `apps/edge/internal/service/provider_resolution.go`, `apps/edge/internal/service/provider_pool.go`, `apps/edge/internal/service/run_submit.go`, `apps/edge/internal/service/provider_tunnel.go`
- 테스트: `apps/edge/internal/service/provider_pool_admission_test.go`, `apps/edge/internal/service/service_internal_test.go`, `apps/edge/internal/bootstrap/runtime_refresh_test.go`, `apps/edge/internal/service/status_provider_test.go`
- 테스트 규칙: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/testing-smoke.md`
- 루프/선행 근거: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/plan_local_G06_1.log`, `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/code_review_local_G07_1.log`, `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/03+01,02_cross_group_wakeup/complete.log`, `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/04_provider_pool_policy_config/complete.log`

### SDD 기준

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-resource-admission-ownership-alignment/SDD.md`
- 상태: `[승인됨]`, SDD 잠금 해제.
- 대상: Acceptance Scenario `S06` / Milestone Task `queue-ownership`.
- Evidence Map: S06은 Edge provider resource/queue deterministic tests와 race 검증을 요구하고 최종 `complete.log`의 `Roadmap Completion`에 `queue-ownership`과 명령 evidence를 남겨야 한다.
- 따라서 구현 범위는 two-candidate AB/BA, 실제 legacy pending, 명시 cancel과 독립 root timeout을 직접 만드는 fixture/assertion으로 제한하고 focused/race/Edge 전체 검증을 final evidence로 사용한다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/testing-smoke.md`를 읽었다. 구조적 공백과 `<확인 필요>` 값은 없다.
- fresh 실행을 위해 Go 검증에 `-count=1`과 `/tmp` GOCACHE를 사용하고 concurrency 회귀는 `-race -count=10`을 사용한다.
- 프리플라이트: runner=`/config/workspace/iop`, branch=`main`, HEAD=`546a3f8a123cd2fb6f6faf3dbd9c655f71c3d0fd`, Linux/aarch64, Go 1.26.2, dirty shared worktree다. `bash`, `go`, `rg`, routing formatter와 long-context smoke script가 존재한다.
- config=`configs/edge.yaml`; live `/models`와 Control Plane status endpoint는 현재 접근 불가다. preflight rc=3이면 exact output과 out-dir를 blocker evidence로 기록하고 mixed scenario는 실행하지 않는다.

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

- candidate order: `TestProviderPoolCommonQueueTimeoutAcrossCandidateOrders`가 subtest마다 단일 candidate만 사용해 `[A,B]`/`[B,A]`를 검증하지 않는다.
- legacy pending isolation: `TestProviderPoolMaxQueueIgnoresLegacyPending`가 legacy item을 dispatch만 하고 pending queue를 만들지 않는다.
- cancel/timeout recovery: `TestProviderPoolPendingCountRecoversAfterCancelAndTimeout`의 두 waiter가 모두 root 80ms internal timeout으로 끝나 `context.Canceled` 경로가 실행되지 않는다.
- initial/apply handoff, explicit zero, global pending cap, runtime triple snapshot은 기존 focused/race 테스트가 실제 경로를 실행하며 현재 PASS한다.

### 심볼 참조

- production symbol 변경은 없다.
- service package의 중복 `TestRefreshProviderPoolPolicy`는 같은 package 밖 호출부가 없는 test symbol이며 bootstrap의 동명 회귀가 initial/apply 경로를 소유한다.

### 분할 판단

- split decision policy를 먼저 평가했다. 이번 후속은 기존 `05+03,04_provider_pool_policy_admission`의 동일한 테스트 파일 한 개에서 fixture/assertion 세 개와 중복 테스트를 정리하는 단일 evidence unit이다.
- API/foundation, 도메인, 검증 전략, 독립 완료 경계가 추가되지 않고 세 회귀가 같은 focused/race 명령으로 판정되므로 추가 split은 인위적이다.
- 서브태스크 의존성 `03`, `04`는 각각 `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/03+01,02_cross_group_wakeup/complete.log`, `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/04_provider_pool_policy_config/complete.log`로 충족됐다.

### 범위 결정 근거

- production `apps/edge/internal/service/*.go`, bootstrap/config/configrefresh, contract/spec 문서는 현재 동작이 focused/race 검증을 통과하므로 수정하지 않는다.
- 활성 sibling `06+05_refresh_consistency`, `07+03_snapshot_source`의 source/test와 shared worktree 변경은 이 후속 범위에서 제외한다.
- live provider mixed scenario는 endpoint blocker가 해소되지 않는 한 실행하지 않고 preflight evidence만 남긴다.
- repo root의 기존 untracked `0`, `service.test`는 이 selected task의 소유권이 확인되지 않았으므로 수정·삭제하지 않는다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`.
- build grade scores: scope_coupling=0, state_concurrency=2, blast_irreversibility=0, evidence_diagnosis=2, verification_complexity=1.
- build: lane=`local`, grade=`G05`, filename=`PLAN-local-G05.md`.
- review closures: scope/context/verification/evidence/ownership/decision 모두 `true`.
- review grade scores: scope_coupling=1, state_concurrency=2, blast_irreversibility=0, evidence_diagnosis=2, verification_complexity=1.
- review: lane=`local`, grade=`G06`, filename=`CODE_REVIEW-local-G06.md`.

## 구현 체크리스트

- [ ] `provider_pool_admission_test.go`의 candidate-order, legacy-pending 격리, cancel/timeout recovery fixture를 실제 SDD S06 상태로 고치고, 중복 service-level refresh 테스트를 제거하거나 고유 lock-safe assertion만 흡수한 뒤 focused/race/Edge 전체/preflight 검증을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_PROVIDER_POLICY_EVIDENCE-1] S06 deterministic evidence를 실제 경합으로 복구

#### 문제

- `apps/edge/internal/service/provider_pool_admission_test.go:131-180`은 두 provider 후보 순서를 뒤집지 않는다.
- `apps/edge/internal/service/provider_pool_admission_test.go:243-326`은 명시 cancel 없이 두 waiter가 같은 root timeout으로 끝난다.
- `apps/edge/internal/service/provider_pool_admission_test.go:333-385`는 legacy pending item이 없다.
- `apps/edge/internal/service/provider_pool_admission_test.go:387-434`는 bootstrap initial/apply 회귀와 중복되고 queue 내부 policy를 lock 없이 읽는다.

#### 해결 방법

candidate-order fixture는 provider A/B가 모두 후보인 slice 두 개를 만들고 두 resource를 먼저 포화시킨 뒤 같은 root timeout을 검증한다.

```go
orders := []struct {
	name       string
	candidates []candidateNode
}{
	{name: "A_then_B", candidates: []candidateNode{candA, candB}},
	{name: "B_then_A", candidates: []candidateNode{candB, candA}},
}
```

legacy isolation fixture는 provider id가 없는 legacy candidate의 slot을 채운 뒤 동일 group에 실제 waiter를 enqueue하고, 별도 provider-pool waiter가 global pending cap에 들어갈 수 있음을 확인한다. recovery fixture는 root timeout 전에 한 context를 명시적으로 cancel하고 다른 waiter는 `errQueueTimeout`까지 유지해 error identity와 최종 count를 분리 검증한다.

```go
cancelCtx, cancel := context.WithCancel(t.Context())
go admit(cancelCtx, ...)
go admit(t.Context(), ...)
requireProviderPoolPending(t, m, 2)
cancel()
```

중복 service-level `TestRefreshProviderPoolPolicy`는 제거한다. 고유 assertion이 발견되면 bootstrap `TestRefreshProviderPoolPolicy` 또는 lock-safe service helper 기반 회귀에만 흡수한다.

#### 수정 파일 및 체크리스트

- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: AB/BA 두 후보를 실제로 포화시키고 root common timeout과 동일 error identity를 검증한다.
- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: provider id 없는 legacy group에 실제 pending waiter를 만들고 provider-pool pending count/global cap과 격리됨을 검증한다.
- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: 한 waiter는 `context.Canceled`, 다른 waiter는 `errQueueTimeout`으로 끝나게 하고 pending/lease/resource 회복을 검증한다.
- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: 중복 service-level refresh 테스트를 제거하거나 고유 assertion만 lock-safe 경로에 흡수한다.

#### 테스트 작성

- 수정: `TestProviderPoolCommonQueueTimeoutAcrossCandidateOrders` — 두 provider가 동시에 존재하고 AB/BA 순서 모두 root timeout을 사용하는지 확인한다.
- 수정: `TestProviderPoolMaxQueueIgnoresLegacyPending` — 실제 legacy pending item이 global provider-pool count에 포함되지 않는지 확인한다.
- 수정: `TestProviderPoolPendingCountRecoversAfterCancelAndTimeout` — 명시 cancel과 독립 internal timeout의 error identity 및 최종 회복을 확인한다.
- 제거/흡수: service package 중복 `TestRefreshProviderPoolPolicy` — bootstrap initial/apply regression과 중복 evidence를 정리한다.

#### 중간 검증

```bash
gofmt -d apps/edge/internal/service/provider_pool_admission_test.go
```

출력 없음.

```bash
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence go test -v -count=1 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
```

세 테스트와 AB/BA subtest가 실제 `=== RUN`으로 실행되고 PASS한다.

```bash
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence go test -race -count=10 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
```

PASS하고 race warning이 없다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/service/provider_pool_admission_test.go` | REVIEW_PROVIDER_POLICY_EVIDENCE-1 |

## 최종 검증

fresh 실행이 필요하므로 Go test cache 결과만 재사용하지 않고 `-count=1` 또는 명시 반복 count를 사용한다.

```bash
gofmt -d apps/edge/internal/service/provider_pool_admission_test.go
git diff --check
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence go test -v -count=1 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence go test -race -count=10 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence go test -count=1 ./apps/edge/...
GOCACHE=/tmp/iop-go-cache-provider-policy-evidence scripts/e2e-long-context-admission-smoke.sh --preflight --out-dir /tmp/iop-long-admission-smoke-provider-policy-evidence
```

- gofmt/diff/focused/race/Edge 전체는 PASS해야 한다.
- preflight rc=0이면 필요한 live scenario를 이어서 실행한다. rc=3이면 exact blocker output과 evidence out-dir를 기록하고 성공으로 바꾸지 않는다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
