<!-- task=m-provider-resource-admission-ownership-alignment/07+03_snapshot_source plan=4 tag=REVIEW_REVIEW_REVIEW_REVIEW_PROVIDER_SNAPSHOT_SOURCE -->

# Provider Snapshot Exact Tuple 회귀 신뢰성 보완 계획

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

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

## 배경

queue → service 선형화 구현과 Go 회귀는 통과하지만, 이를 증명해야 할 forced-order fixture가 provider를 실제로 disable하지 않고 old queued pressure도 만들지 않는다. concurrent helper도 이전 리뷰에서 재현된 torn `available/capacity=1/in_flight=1/queued=0`을 허용하고 failure channel이 회귀 발생 시 교착할 수 있다. production 코드는 유지하고 exact tuple fixture·assertion·bounded failure 수집만 보완해 SDD S07 evidence를 신뢰 가능하게 만든다.

## 사용자 리뷰 요청 흐름

선택된 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/07+03_snapshot_source`
- Archived plan: `agent-task/m-provider-resource-admission-ownership-alignment/07+03_snapshot_source/plan_local_G06_3.log`
- Archived review: `agent-task/m-provider-resource-admission-ownership-alignment/07+03_snapshot_source/code_review_local_G06_3.log`
- Verdict: FAIL
- Findings: Required 3, Suggested 0, Nit 0.
  - forced-order fixture의 `disabledStore`가 `Enabled=nil`이라 실제 enabled이고 queued waiter도 없어 200회 모두 같은 available 상태여도 통과한다.
  - concurrent available helper가 `in_flight` 또는 `queued` 중 하나만 양수면 허용해 torn candidate pressure를 거부하지 않는다.
  - reader goroutine이 반복 `t.Errorf`와 소비 전 blocking channel send를 사용해 계획한 first-error bounded collection을 충족하지 않는다.
- Affected files: `apps/edge/internal/service/status_provider_test.go`.
- Verification evidence:
  - exact test 단독, exact/concurrent 20회, targeted snapshot suite, race 5회와 `go test -count=1 -timeout 120s ./apps/edge/...`는 모두 PASS했지만 assertion 결함 때문에 논리적 원자성 증거가 아니다.
  - provider-pool preflight는 config check PASS 후 model/status endpoint 접근 불가로 BLOCKED(rc=3), evidence=`/tmp/iop-long-admission-smoke-review-snapshot-source`.
- Roadmap carryover: `snapshot-source`는 exact old `available/1/1` 또는 new `disabled/0/0`만 허용하는 deterministic ordering 회귀와 bounded concurrent failure 수집이 race/Edge 전체 검증과 함께 통과할 때만 완료 대상으로 유지한다.
- Narrow reread allowed: 위 `plan_local_G06_3.log`, `code_review_local_G06_3.log` 두 파일만 필요 시 읽는다.

## 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:
  - `snapshot-source`: provider lease state와 live candidate pressure 기반 snapshot
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- 규칙/라우팅: `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/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`
- 활성/이전 루프: `agent-task/m-provider-resource-admission-ownership-alignment/07+03_snapshot_source/plan_local_G06_3.log`, `agent-task/m-provider-resource-admission-ownership-alignment/07+03_snapshot_source/code_review_local_G06_3.log`
- 선행 완료: `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/03+01,02_cross_group_wakeup/complete.log`
- 로드맵/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/control/control-plane-operations.md`, `agent-contract/index.md`, `agent-contract/inner/edge-config-runtime-refresh.md`, `agent-contract/inner/control-plane-edge-wire.md`
- source context: `apps/edge/internal/service/service.go`, `apps/edge/internal/service/status_provider.go`, `apps/edge/internal/service/model_queue_snapshot.go`, `apps/edge/internal/service/model_queue_types.go`, `apps/edge/internal/service/provider_resolution.go`, `apps/edge/internal/service/provider_pool.go`, `apps/edge/internal/bootstrap/runtime.go`, `apps/edge/internal/node/registry.go`, `apps/edge/internal/node/store.go`, `apps/edge/internal/transport/connection_handlers.go`, `packages/go/config/provider_types.go`
- 변경 대상 test 전체: `apps/edge/internal/service/status_provider_test.go`
- 테스트 규칙/프로필: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/testing-smoke.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-resource-admission-ownership-alignment/SDD.md`, 상태 `[승인됨]`, 잠금 `해제`.
- 대상: Acceptance Scenario S07 / Milestone Task `snapshot-source`.
- S07은 실행·대기·반환 중 일반·long in-flight가 lease state와 같고 queued/long_queued가 live candidate pressure여야 한다.
- Evidence Map의 S07 row는 deterministic Edge provider resource/queue tests와 race 검증을 요구한다. 따라서 exact enabled/disabled tuple, 양 상태 실제 관찰, bounded failure collection을 구현 체크리스트에 넣고 반복·race·Edge 전체 검증을 최종 evidence로 고정한다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/testing-smoke.md`가 존재해 모두 읽었다.
- fresh Go test는 `/tmp` GOCACHE와 `-count=1` 또는 명시 반복 횟수를 사용한다. 변경 파일 format/diff, exact test existence, deterministic 20회, snapshot lifecycle, race 5회, Edge 전체 회귀를 적용한다.
- 테스트 환경 프리플라이트: runner `/config/workspace/iop`, branch `main`, HEAD `e4966b0b173d823f6fb9f0a995da5d6070915bcf`, Linux aarch64, sibling subtask 변경이 있는 dirty worktree다. source는 현재 checkout을 그대로 검증하며 repo-local `service.test`와 sibling 변경을 수정하지 않는다. config는 `configs/edge.yaml`이고 config check는 PASS했다. model endpoint `http://toki-labs.com:18083/v1/models`와 status endpoint `http://127.0.0.1:18001/edges/edge-toki-labs-dev/status`는 접근 불가해 preflight rc=3이며 evidence는 `/tmp/iop-long-admission-smoke-review-snapshot-source`다. 이는 별도 `capacity-smoke` 책임의 환경 blocker이며 test-only 후속 구현 결정은 막지 않는다.

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

- forced-order 회귀는 `disabledStore.Enabled=nil`과 waiter 부재 때문에 old/new 상태를 구분하지 못하고 양 상태 sample을 확인하지 않는다.
- concurrent storm은 provider별 configured capacity, in-flight, queued, long tuple을 exact 비교하지 않아 torn `queued=0`을 허용한다.
- failure path는 reader당 첫 오류 한 건으로 제한되지 않고 channel 소비가 join 뒤라 unexpected status 반복 시 교착할 수 있다.
- normal/long lifecycle, resolver error/empty, production lock order, race와 Edge 전체 회귀는 기존 테스트가 유지한다.

### 심볼 참조

- rename/remove 대상은 없다.
- 수정 대상 helper/test 참조는 같은 파일의 `assertAvailableTuple`, `assertDisabledTuple`, `TestProviderSnapshotConcurrentRefreshPressureUsesLiveCandidates`, `TestProviderSnapshotRuntimeRefreshIsOldOrNew`다.

### 분할 판단

- split decision policy를 재평가했다. 기존 split subtask `07+03_snapshot_source`를 유지하며 새 sibling을 만들지 않는다.
- 이번 후속은 한 테스트 파일의 동일 old/new tuple 불변식과 failure collection을 함께 수정한다. API/production 소유권, 별도 검증 전략, 독립 산출물 경계가 없어 추가 split은 fixture/helper 조정을 인위적으로 분리한다.
- predecessor index `03`은 `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/03+01,02_cross_group_wakeup/complete.log`의 PASS로 충족됐다.

### 범위 결정 근거

- `apps/edge/internal/service/status_provider_test.go`만 수정한다.
- production lock/order 구현, provider policy, queue scheduling, disconnect/offline snapshot, config/contract/spec/roadmap/SDD 본문은 변경하지 않는다.
- sibling subtask 파일과 repo-local `service.test` artifact를 수정·삭제하지 않는다.
- live endpoint 복구와 capacity 1 field smoke는 별도 `capacity-smoke` Task 책임이다.

### 최종 라우팅

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

## 의존 관계 및 구현 순서

1. exact tuple validator와 deterministic forced-order fixture를 먼저 보완한다.
2. 같은 validator를 concurrent storm에 적용하고 reader별 first-error bounded collection을 완성한다.
3. 반복·race·Edge 전체 검증과 provider-pool preflight를 실행한다.

## 구현 체크리스트

- [ ] forced-order fixture가 explicit enabled/disabled store와 old queued pressure를 만들고 production `ListNodeSnapshots`에서 exact old `available/1/1` 및 new `disabled/0/0`을 모두 관찰하며 그 외 tuple을 거부하게 한다.
- [ ] concurrent storm이 provider별 전체 tuple을 exact 검증하고 각 reader가 첫 invalid tuple만 bounded/nonblocking 값으로 전달한 뒤 종료하게 하며 반복·race·Edge 전체 검증과 provider-pool preflight 실제 출력을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_PROVIDER_SNAPSHOT_SOURCE-1] Deterministic exact old/new tuple fixture

#### 문제

`apps/edge/internal/service/status_provider_test.go:1521-1530`의 `disabledStore`는 `Enabled=nil`이라 `config.ProviderEnabled` 계약상 enabled다. `:1534-1546`은 holder만 만들고 queued waiter가 없으며, `:1593-1614`는 available tuple에서 in-flight 또는 queued 하나만 양수면 허용하고 양 상태 관찰도 확인하지 않는다.

#### 해결 방법

explicit true/false provider store를 만들고 enabled 상태마다 resource in-flight 1과 live candidate queue item 1을 seed한다. test-local queue/service lock barrier와 `TryLock` 기반 bounded poll로 production reader가 queue lock을 잡고 service lock에서 대기한 사실을 확인한 뒤 writer를 시작한다. service barrier를 해제해 reader의 exact old tuple을 받고 writer 완료 뒤 production surface의 exact disabled zero tuple을 확인한다. sleep은 ordering proof로 사용하지 않는다.

Before (`status_provider_test.go:1521-1530`, `:1593-1610`):

```go
disabledStore := edgenode.NewNodeStore()
disabledStore.Add(&edgenode.NodeRecord{
	Providers: []config.NodeProviderConf{{ID: "prov-x", Capacity: 1}},
})

if got.capacity == 1 && got.status == "available" {
	if got.inFlight == 0 && got.queued == 0 { /* fail */ }
}
```

After:

```go
enabled, disabled := true, false
enabledStore := snapshotRefreshStore(&enabled)
disabledStore := snapshotRefreshStore(&disabled)

seedOldTuple(svc, 1, 1)
svc.mu.Lock()
go readProductionSnapshot()
waitUntilQueueLockHeldWithBoundedTryLock(t, svc.queue)
go applyDisabledRuntime()
svc.mu.Unlock()
assertExactOldTuple(t, <-readResult)
<-writerDone
assertExactDisabledTuple(t, svc.ListNodeSnapshots())
```

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

- [ ] `apps/edge/internal/service/status_provider_test.go`: explicit true/false stores와 exact tuple test fixture를 만든다.
- [ ] `apps/edge/internal/service/status_provider_test.go`: old resource/queue pressure를 매 iteration 정확히 seed하고 production reader lock acquisition을 bounded barrier로 증명한다.
- [ ] `apps/edge/internal/service/status_provider_test.go`: exact old/new tuple과 양 상태 관찰을 assertion한다.

#### 테스트 작성

- 수정: `TestProviderSnapshotRuntimeRefreshIsOldOrNew`.
- fixture: capacity 1 provider, in-flight 1 resource, live resolver가 포함된 queued item 1, explicit false refresh store.
- assertion: old는 `status=available/capacity=1/in_flight=1/queued=1`, new는 disabled effective zero tuple이며 reader가 queue-first가 아니면 barrier 자체가 실패한다.

#### 중간 검증

```bash
gofmt -d apps/edge/internal/service/status_provider_test.go
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -list '^TestProviderSnapshotRuntimeRefreshIsOldOrNew$' ./apps/edge/internal/service | rg --fixed-strings 'TestProviderSnapshotRuntimeRefreshIsOldOrNew'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -count=20 -timeout 60s ./apps/edge/internal/service -run '^TestProviderSnapshotRuntimeRefreshIsOldOrNew$'
```

기대 결과: format diff 없음, exact test name 존재, deterministic old/new tuple 회귀 20회 PASS.

### [REVIEW_REVIEW_REVIEW_REVIEW_PROVIDER_SNAPSHOT_SOURCE-2] Concurrent exact assertion과 bounded failure collection

#### 문제

`apps/edge/internal/service/status_provider_test.go:787-838`의 helper는 configured exact 값 대신 양수/비음수 범위만 검사한다. `:1180-1264`는 reader goroutine에서 반복 `t.Errorf`를 호출하고 2,000칸 channel을 `wg.Wait` 뒤 소비하므로 regression이 반복되면 blocking send로 join이 멈출 수 있다. `seenAvailable`도 fallback provider가 아니라 항상 available인 shared provider만으로 true가 될 수 있다.

#### 해결 방법

testing.T를 받지 않는 pure tuple validator가 provider별 expected field 전부를 비교하고 오류 값을 반환하게 한다. shared provider는 항상 exact available tuple, fallback provider는 exact available 또는 disabled zero tuple만 허용한다. 상태 sample은 fallback provider의 available/disabled를 각각 기록한다. 각 reader는 첫 오류만 reader 수 크기의 channel에 nonblocking 전달하고 즉시 종료하며 main goroutine만 `t.Fatal/Error`를 호출한다.

Before (`status_provider_test.go:793-810`, `:1233-1250`):

```go
if ps.GetCapacity() <= 0 { t.Errorf(...) }
if ps.GetInFlight() == 0 && ps.GetQueued() == 0 { t.Errorf(...) }

switch ps.GetStatus() {
case "available":
	assertAvailableTuple(t, ps)
default:
	errCh <- snapAssert{pid, ...}
}
```

After:

```go
func validateSnapshotTuple(ps *iop.ProviderSnapshot, want snapshotTuple) error {
	// Compare status, capacity, inFlight, queued, and both long counters exactly.
}

errCh := make(chan snapshotFailure, snapReaders)
if err := validateProviderState(ps); err != nil {
	select {
	case errCh <- snapshotFailure{reader: readerID, err: err}:
	default:
	}
	return
}
```

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

- [ ] `apps/edge/internal/service/status_provider_test.go`: exact pure tuple validator로 loose helper를 교체한다.
- [ ] `apps/edge/internal/service/status_provider_test.go`: fallback provider의 available/disabled 두 상태를 별도로 sample한다.
- [ ] `apps/edge/internal/service/status_provider_test.go`: reader별 첫 오류 한 건, bounded/nonblocking delivery, main-goroutine assertion으로 정리한다.
- [ ] `apps/edge/internal/service/status_provider_test.go`: normal/long lifecycle과 resolver error/empty 회귀 의미를 보존한다.

#### 테스트 작성

- 수정: `TestProviderSnapshotConcurrentRefreshPressureUsesLiveCandidates`.
- assertion: enabled fallback은 `available/1/1/1/0/0`, disabled fallback은 모든 effective field 0, shared provider는 storm 전체에서 `available/1/1/1/0/0`이다.
- failure-path fixture는 별도 production 변경 없이 validator 반환값과 reader별 단일 보고 구조로 검토 가능하게 둔다.

#### 중간 검증

```bash
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -count=20 -timeout 60s ./apps/edge/internal/service -run '^TestProviderSnapshot(RuntimeRefreshIsOldOrNew|ConcurrentRefreshPressureUsesLiveCandidates)$'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -race -count=5 -timeout 60s ./apps/edge/internal/service -run '^TestProviderSnapshot(RuntimeRefreshIsOldOrNew|LiveCandidatePressureFollowsRefreshAcrossModelGroups|ConcurrentRefreshPressureUsesLiveCandidates|QueuedCountsCandidatePressureAcrossModelGroups)$'
```

기대 결과: exact/concurrent 20회와 race 5회가 assertion failure, deadlock, race detector 경고 없이 PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/service/status_provider_test.go` | REVIEW_REVIEW_REVIEW_REVIEW_PROVIDER_SNAPSHOT_SOURCE-1, REVIEW_REVIEW_REVIEW_REVIEW_PROVIDER_SNAPSHOT_SOURCE-2 |

## 최종 검증

```bash
gofmt -d apps/edge/internal/service/status_provider_test.go
git diff --check -- apps/edge/internal/service/status_provider_test.go
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -list '^TestProviderSnapshotRuntimeRefreshIsOldOrNew$' ./apps/edge/internal/service | rg --fixed-strings 'TestProviderSnapshotRuntimeRefreshIsOldOrNew'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -count=20 -timeout 60s ./apps/edge/internal/service -run '^TestProviderSnapshot(RuntimeRefreshIsOldOrNew|ConcurrentRefreshPressureUsesLiveCandidates)$'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -count=1 ./apps/edge/internal/service -run '^TestProviderSnapshot(ResolverErrorDoesNotUseStaleCandidate|RuntimeRefreshIsOldOrNew|LiveCandidatePressureFollowsRefreshAcrossModelGroups|ConcurrentRefreshPressureUsesLiveCandidates|QueuedCountsCandidatePressureAcrossModelGroups|ReadsResourceState|IgnoresLegacyFallbackState)$'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -race -count=5 -timeout 60s ./apps/edge/internal/service -run '^TestProviderSnapshot(RuntimeRefreshIsOldOrNew|LiveCandidatePressureFollowsRefreshAcrossModelGroups|ConcurrentRefreshPressureUsesLiveCandidates|QueuedCountsCandidatePressureAcrossModelGroups)$'
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup go test -count=1 -timeout 120s ./apps/edge/...
GOCACHE=/tmp/iop-go-cache-provider-snapshot-exact-followup scripts/e2e-long-context-admission-smoke.sh --preflight --out-dir /tmp/iop-long-admission-smoke-provider-snapshot-exact-followup
```

기대 결과: format/diff/exact existence/deterministic/targeted/race/Edge 전체 PASS. preflight는 endpoint 가용 시 PASS하고, 동일 endpoint 미가용 시 rc=3과 실제 blocker stdout/stderr/evidence 경로를 기록한다.

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