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

# Provider-pool Legacy Cap 경계와 Race Barrier 후속 계획

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

구현과 검증을 완료한 뒤 `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은 사용자 리뷰 요청이 아니다.

## 배경

production-reachable legacy group pending과 provider-pool pending 분리는 복구됐지만, 실제 global cap 경계에서 legacy pending이 제외되는 admission 결과는 실행하지 않았다. 필수 세-test race 검증도 짧은 80ms timeout 안에 두 goroutine이 모두 queue에 진입한다고 가정해 runner load가 겹친 fresh 실행에서 실패했다. 이 후속은 production 코드를 바꾸지 않고 cap-boundary assertion과 deterministic cancel/timeout barrier만 복구한다.

## 사용자 리뷰 요청 흐름

선택된 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_3.log`
- Prior review: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/code_review_local_G07_3.log`
- Verdict: FAIL
- Findings: Required 2, Suggested 0, Nit 0.
- Affected files: `apps/edge/internal/service/provider_pool_admission_test.go`.
- Verification evidence:
  - gofmt, diff check, focused non-race 세 테스트와 Edge 전체는 reviewer fresh PASS했다.
  - legacy isolation 단독 race count=20, recovery 단독 race count=20, 기존 cap+legacy 비-race count=20은 PASS했다.
  - 세-test race count=10은 runner load가 겹친 실행에서 pending=2 barrier가 성립하지 않아 FAIL했고 isolated retry는 PASS해 timing-dependent flake가 확인됐다.
  - legacy pending과 provider-pool pending count는 분리됐지만 같은 fixture에서 다음 admission의 실제 `errQueueFull` cap 경계는 실행하지 않았다.
  - live preflight는 config check PASS 후 외부 `/models`와 Control Plane status endpoint 접근 불가로 rc=3이었다.
- Roadmap carryover: `queue-ownership`은 미완료이며 승인된 SDD S06의 legacy-pending cap 경계와 신뢰 가능한 race 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/service/model_queue_types.go`, `apps/edge/internal/service/model_queue_admission.go`, `apps/edge/internal/service/model_queue_release.go`, `apps/edge/internal/service/provider_resolution.go`
- 테스트: `apps/edge/internal/service/provider_pool_admission_test.go`, `apps/edge/internal/service/model_queue_test_support_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/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`
- 루프 근거: `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/plan_local_G06_3.log`, `agent-task/m-provider-resource-admission-ownership-alignment/05+03,04_provider_pool_policy_admission/code_review_local_G07_3.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를 남겨야 한다.
- 따라서 구현 체크리스트는 legacy pending이 존재하는 실제 global cap admission 경계와 cancel/timeout queue 복구의 결정적 순서 증거로 제한하고, focused/race/Edge 전체 및 preflight 결과를 최종 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 검증은 `/tmp` GOCACHE와 `-count=1` 또는 명시 반복 count를 사용한다.
- 테스트 환경 프리플라이트: runner와 repo root는 `/config/workspace/iop`, branch=`main`, HEAD=`546a3f8a123cd2fb6f6faf3dbd9c655f71c3d0fd`, dirty shared worktree, Linux/aarch64, Go 1.26.2다. `bash`, `go`, routing formatter와 long-context smoke script가 존재한다.
- config=`configs/edge.yaml`; 현재 evidence에서 config check는 PASS했으나 live `/models`와 Control Plane status endpoint는 접근 불가다. preflight rc=3이면 blocker output과 out-dir를 기록하고 성공으로 바꾸지 않는다.

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

- legacy isolation: legacy `providerID` 없는 group-local pending과 별도 provider-pool pending은 현재 테스트가 만든다.
- cap boundary: legacy pending이 존재할 때 첫 provider-pool waiter는 허용되고 다음 provider-pool admission만 `errQueueFull`인지 실제 admission assertion이 없다.
- cancel/timeout recovery: error identity와 최종 counter assertion은 있으나 두 waiter가 80ms 안에 동시에 pending이 된다는 timing 가정 때문에 필수 race suite가 간헐 실패한다.
- candidate-order, explicit zero, initial/apply handoff와 Edge 전체 회귀는 현재 focused/Edge 검증에서 PASS한다.

### 심볼 참조

- renamed/removed production symbol 없음. 기존 테스트 함수명과 helper signature를 유지한다.

### 분할 판단

- split decision policy를 재평가했다. 이번 후속은 기존 split subtask의 동일 테스트 파일에서 cap-boundary와 race barrier를 함께 고치고 같은 focused/race 명령으로 판정하는 단일 evidence unit이다.
- API/foundation, ownership, 도메인, 독립 검증 전략 경계가 추가되지 않아 새 sibling으로 분리하면 같은 fixture와 evidence를 중복 조정하게 된다.
- 디렉터리 의존성 `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`로 충족됐다.

### 범위 결정 근거

- 수정 범위는 `apps/edge/internal/service/provider_pool_admission_test.go`의 두 테스트 fixture/assertion뿐이다.
- production `apps/edge/internal/service/*.go`, config/bootstrap/configrefresh, contract/spec/roadmap 문서는 수정하지 않는다.
- 활성 sibling `06+05_refresh_consistency`, shared worktree의 production 변경, repo root의 `0`과 `service.test`는 소유권이 없으므로 수정·삭제하지 않는다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`.
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`.
- build grade scores: scope_coupling=1, state_concurrency=2, blast_irreversibility=0, evidence_diagnosis=2, verification_complexity=1.
- build: lane=`local`, grade=`G06`, filename=`PLAN-local-G06.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`.

## 구현 체크리스트

- [x] `TestProviderPoolMaxQueueIgnoresLegacyPending`에 legacy pending이 존재하는 실제 global cap 경계의 `errQueueFull` admission을 추가하고, `TestProviderPoolPendingCountRecoversAfterCancelAndTimeout`을 staged enqueue barrier와 충분한 독립 timeout으로 안정화한 뒤 focused/race/Edge 전체/preflight 검증을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_PROVIDER_POLICY_CAP_BARRIER-1] Legacy cap 경계와 deterministic cancel/timeout 증거 복구

#### 문제

- `apps/edge/internal/service/provider_pool_admission_test.go:392,438-453`은 root `max_queue=2`에서 provider-pool waiter 한 건의 helper count만 확인하고 다음 admission의 실제 `errQueueFull` cap 경계를 실행하지 않는다.
- `apps/edge/internal/service/provider_pool_admission_test.go:277,289-302`는 80ms root timeout을 가진 두 waiter를 동시에 시작한 뒤 둘 모두 pending이 되기를 기다려 runner load와 race instrumentation에 따라 barrier 전에 item이 만료될 수 있다.

#### 해결 방법

legacy isolation fixture는 기존 production-reachable candidate 분리를 유지하면서 root cap을 1로 두고 provider-pool waiter 한 건이 queue에 들어간 뒤 다음 admission이 즉시 `errQueueFull`인지 직접 확인한다.

```go
// Before: provider_pool_admission_test.go:392,438-453
m.setProviderPoolPolicyLocked(store, NewGroupPolicy(2, 30*time.Second))
go func() {
	_, err := m.admit(ctxPp, "pp-waiter", "", "", providerCandidates, groupPolicy{}, nil, false, true)
	chPp <- err
}()
requireProviderPoolPending(t, m, 1)
```

```go
// After
m.setProviderPoolPolicyLocked(store, NewGroupPolicy(1, 30*time.Second))
go func() {
	_, err := m.admit(ctxPp, "pp-waiter", "", "", providerCandidates, groupPolicy{}, nil, false, true)
	chPp <- err
}()
requireProviderPoolPending(t, m, 1)

ctxCap, cancelCap := context.WithTimeout(t.Context(), 500*time.Millisecond)
defer cancelCap()
started := time.Now()
_, err := m.admit(ctxCap, "pp-over-cap", "", "", providerCandidates, groupPolicy{}, nil, false, true)
if !errors.Is(err, errQueueFull) {
	t.Fatalf("expected legacy-excluded provider-pool errQueueFull, got %v", err)
}
if elapsed := time.Since(started); elapsed > 200*time.Millisecond {
	t.Fatalf("errQueueFull was not immediate: %v", elapsed)
}
```

recovery fixture는 waiter를 하나씩 시작하고 각 enqueue를 관측한 뒤 다음 waiter를 시작한다. root timeout과 result wait bound를 race build에서 두 barrier가 끝나기 전에 만료되지 않을 만큼 늘리되, 두 번째 waiter가 독립 internal timeout으로 끝난다는 assertion은 유지한다.

```go
// Before: provider_pool_admission_test.go:277,289-302
m.setProviderPoolPolicyLocked(store, NewGroupPolicy(2, 80*time.Millisecond))
go admit(cancelCtx)
go admit(timeoutCtx)
requireProviderPoolPending(t, m, 2)
```

```go
// After
m.setProviderPoolPolicyLocked(store, NewGroupPolicy(2, time.Second))
go admit(cancelCtx)
requireProviderPoolPending(t, m, 1)
go admit(timeoutCtx)
requireProviderPoolPending(t, m, 2)
cancel()
// cancel waiter => context.Canceled
// timeout waiter => errQueueTimeout within a bound greater than one second
```

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

- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: legacy pending을 유지한 상태에서 root cap 1, 첫 provider-pool pending 1, 다음 admission `errQueueFull`을 직접 검증한다.
- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: recovery waiter를 staged enqueue하고 내부 timeout/result bound를 race instrumentation에 안전하게 조정한다.
- [ ] `apps/edge/internal/service/provider_pool_admission_test.go`: cancel/timeout identity와 최종 pending, lease, legacy group/resource counter 0 회복 assertion을 유지한다.

#### 테스트 작성

- 수정: `TestProviderPoolMaxQueueIgnoresLegacyPending` — legacy pending이 cap에 포함되지 않아 첫 provider-pool waiter가 queue에 들어가며 그 다음 provider-pool request만 `errQueueFull`인지 검증한다.
- 수정: `TestProviderPoolPendingCountRecoversAfterCancelAndTimeout` — staged pending barrier 뒤 명시 cancel과 독립 queue timeout이 결정적으로 분리되는지 검증한다.
- 신규 production 테스트나 production source 변경은 없다.

#### 중간 검증

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

출력 없음.

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

정확히 네 테스트와 AB/BA subtest가 실행되고 PASS한다.

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

PASS하고 pending barrier 실패, race warning, 간헐 실패가 없다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/service/provider_pool_admission_test.go` | REVIEW_PROVIDER_POLICY_CAP_BARRIER-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-cap-barrier go test -v -count=1 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueCountsPendingAcrossGroups|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
GOCACHE=/tmp/iop-go-cache-provider-policy-cap-barrier go test -race -count=20 ./apps/edge/internal/service -run '^TestProviderPool(CommonQueueTimeoutAcrossCandidateOrders|MaxQueueCountsPendingAcrossGroups|MaxQueueIgnoresLegacyPending|PendingCountRecoversAfterCancelAndTimeout)$'
GOCACHE=/tmp/iop-go-cache-provider-policy-cap-barrier go test -count=1 ./apps/edge/...
GOCACHE=/tmp/iop-go-cache-provider-policy-cap-barrier scripts/e2e-long-context-admission-smoke.sh --preflight --out-dir /tmp/iop-long-admission-smoke-provider-policy-cap-barrier
```

- gofmt/diff/focused/race/Edge 전체는 PASS해야 한다.
- focused stdout에는 정확히 네 테스트와 candidate-order AB/BA subtest만 있어야 한다.
- preflight rc=0이면 필요한 live scenario를 이어서 실행한다. rc=3이면 exact blocker output과 evidence out-dir를 기록하고 성공으로 바꾸지 않는다.

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