<!-- task=m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple plan=1 tag=REVIEW_REFACTOR -->

# Authoritative reconnect admission regression

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

검증을 실행하고 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용과 stdout/stderr를 채운 뒤 active pair를 유지한 채 구현 실행을 종료한다. 저장된 filled-review-without-verdict 상태가 runtime의 `review-ready` 신호이며 별도 handoff 파일·사용자 메시지를 만들지 않는다. code-review 실행, 역할 전환, 사용자 직접 질문·선택지 제시·`request_user_input` 호출, 판정 append, review 전용 섹션 수정, archive/log rename, `USER_REVIEW.md`·`complete.log` 작성, 후속 pair 생성·구현은 금지한다. runtime은 같은 agent의 새 turn·resume·compaction이 아닌 별도 review-agent instance를 즉시 배정한다. 선택 Milestone의 `구현 잠금 > 결정 필요`가 구현을 차단할 때만 review stub의 `사용자 리뷰 요청`에 정확한 연결 근거를 기록하고 같은 file-state handoff를 사용한다. 환경·secret·service blocker, 일반 범위 변경, 자동 후속으로 보강 가능한 evidence 공백은 사용자-facing stop이 아니라 blocker evidence·결정적 해제 조건·재검증을 가진 일반 follow-up 범위다.

## 배경

이전 리뷰는 offline tuple 구현 자체가 아니라 reconnect 회귀 증거가 잘못된 상태를 정상값으로 고정한 점을 발견했다. 현재 테스트는 lease를 만들지 않고 resource counter를 직접 주입한 뒤 registry map만 교체하며, 새 generation이 이전 counter를 그대로 복구하는 것을 기대한다. authoritative disconnect settlement와 실제 신규 admission을 통과하는 최소 회귀로 교체해야 SDD S14와 계획의 완료 조건을 판단할 수 있다.

## 사용자 리뷰 요청 흐름

선택 Milestone의 기존 `구현 잠금 > 결정 필요`만 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 active review stub의 `사용자 리뷰 요청`에 기록한다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 입력 도구를 호출하지 않으며, 요청 검증과 실제 `USER_REVIEW.md` 작성은 별도 code-review 에이전트가 소유한다.

## Archive Evidence Snapshot

- Prior task: `agent-task/m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple`
- Prior verdict: FAIL
- Archived plan: `agent-task/m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple/plan_local_G06_0.log`
- Archived review: `agent-task/m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple/code_review_local_G06_0.log`
- Findings: Required 1, Suggested 0, Nit 0. `TestProviderSnapshotReconnectRestoresCapacityAndAdmission`이 lease/admission 없이 counter를 직접 주입하고 authoritative disconnect를 우회한 뒤 이전 `inflight=2`, `long_inflight=1`을 새 generation 정상값으로 기대한다.
- Affected files: `apps/edge/internal/service/status_provider_test.go`; 구현 검증이 드러내는 최소 필요가 있을 때만 관련 `apps/edge/internal/service/model_queue_snapshot.go`를 변경한다.
- Verification evidence: format/diff check, snapshot regex, service race는 PASS. Edge race는 병렬 자원 경합 중 heartbeat test가 1회 timeout했으나 해당 test 단독과 순차 `go test -race -count=1 ./apps/edge/...` 재실행은 PASS. `scripts/e2e-long-context-admission-smoke.sh --preflight`는 provider/status endpoint 미가용으로 rc=3 BLOCKED였으며 재검증 해제 조건은 두 endpoint reachability다.
- Roadmap carryover: 이 split child는 `offline-snapshot`의 부분 evidence이며 `Roadmap Targets`를 갖지 않는다. Milestone SDD S14의 이전 generation orphan 제거, 새 generation configured capacity와 admission 수렴만 검증한다.

## 분석 결과

### 읽은 파일

- `agent-task/m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple/plan_local_G06_0.log`
- `agent-task/m-provider-resource-admission-ownership-alignment/14+10_offline_provider_tuple/code_review_local_G06_0.log`
- `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/runtime/provider-pool-config-refresh.md`
- `agent-spec/control/control-plane-operations.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-contract/inner/control-plane-edge-wire.md`
- `apps/edge/internal/service/status_provider_test.go`의 `TestProviderSnapshotOfflineVsDisabled`, `TestProviderSnapshotReconnectRestoresCapacityAndAdmission` 범위
- `apps/edge/internal/service/model_queue_snapshot.go`
- `apps/edge/internal/service/status_provider.go`
- `apps/edge/internal/service/model_queue_release.go`의 generation fence/release 범위
- `apps/edge/internal/service/model_queue_admission.go`의 generation eligibility/admit 범위
- `apps/edge/internal/service/model_queue_types.go`의 candidate/lease/resource 상태 범위
- `apps/edge/internal/service/provider_resolution.go`의 provider-pool candidate 범위
- `apps/edge/internal/service/service.go`의 disconnect/runtime snapshot 범위
- `apps/edge/internal/node/registry.go`
- `apps/edge/internal/transport/connection_handlers.go`의 authoritative disconnect listener 범위
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/provider-resource-admission-ownership-alignment/SDD.md`, 상태 `[승인됨]`, 잠금 해제.
- 대상: S14 / Milestone Task `offline-snapshot`.
- Evidence Map: S14는 deterministic connectivity/resource test와 race evidence에서 offline zero tuple, 이전 generation orphan 제거, reconnect configured capacity 및 새 admission 수렴을 요구한다.
- 반영: 테스트가 실제 lease와 authoritative disconnect generation을 사용하고, reconnect 직후 old counter 0 및 신규 lease 뒤 current-generation counter만 노출하는지 같은 회귀에서 검증한다.

### 테스트 환경 규칙

- test_env: local.
- 기준: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, project testing domain rule.
- fresh Go 결과가 필요하므로 모든 test에 `-count=1`을 사용하고 service/Edge race를 재실행한다.
- live provider/status endpoint는 현재 preflight에서 미가용이었다. preflight를 다시 실행하며 rc=3이면 stdout, endpoint reachability 해제 조건, 미실행 scenario를 숨기지 않고 기록한다.

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

- offline enabled/disabled tuple: 기존 테스트가 static/no-queue 및 queue snapshot을 커버한다.
- authoritative disconnect settlement: 현재 reconnect 테스트는 `HandleNodeDisconnect`를 호출하지 않아 미커버다.
- same identity 새 generation admission: 현재 테스트는 admission/lease API를 호출하지 않아 미커버다.
- old generation counter 제거와 새 lease counter 구분: 현재 assertion이 이전 수동 counter 복구를 기대해 반대로 고정한다.

### 심볼 참조

- renamed/removed symbol: 없음.
- follow-up은 기존 `Registry.CurrentGeneration`/`Unregister`, `Service.HandleNodeDisconnect`, `modelQueueManager.admit` 또는 동등한 service admission 경로를 사용한다.

### 분할 판단

- 선택 candidate: reconnect 회귀 fixture 정정.
- 단일 유지: `14+10_offline_provider_tuple` 한 pair만 교체한다.
- write set: 기본 `apps/edge/internal/service/status_provider_test.go`; 검증이 실제 source defect를 증명할 때만 `model_queue_snapshot.go` 최소 수정.
- shared state: registry generation, queue resource/lease map, snapshot read가 한 fixture에서 순서 의존하므로 분리하지 않는다.
- dependencies: 디렉터리 `14+10_...`의 predecessor 10은 기존 archive evidence로 이미 충족됐다. 새 dependency를 만들지 않는다.

### 범위 결정 근거

- Control Plane wire mapping, config/spec/contract 문서 갱신, reconnect waiter pump, live capacity smoke 구현은 이 partial child 밖이다.
- product source 변경은 corrected regression이 실제 defect를 재현할 때만 허용하고, 테스트 정합화만으로 통과하면 확대하지 않는다.

### 최종 라우팅

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

## 구현 체크리스트

- [ ] `TestProviderSnapshotReconnectRestoresCapacityAndAdmission`을 실제 first-generation lease → authoritative generation disconnect settlement → offline zero tuple → higher-generation reconnect → 신규 admission 순서로 교체하고, reconnect 전 old counter가 0이며 신규 lease만 in-flight/long counter에 반영되고 release 뒤 다시 0인지 검증한다.
- [ ] 변경 파일 format/diff, targeted snapshot, service race, Edge race와 long-admission preflight를 실행하고 실제 stdout/stderr 및 외부 blocker의 해제 조건을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우고 active pair를 유지한 채 구현 실행을 종료한다. 이 저장 상태가 runtime의 review-ready 신호이며 별도 handoff 파일·사용자 메시지를 만들지 않는다. 구현 에이전트는 code-review를 실행하거나 review 전용 작업을 수행하지 않는다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REFACTOR-1] Authoritative reconnect admission fixture

문제: `apps/edge/internal/service/status_provider_test.go:2072-2084`는 lease 없이 resource counter를 직접 주입하고, `:2111`에서 registry만 제거하며, `:2173-2177`에서 이전 counter를 새 generation 정상값으로 기대한다. 따라서 테스트 이름과 달리 disconnect release, orphan fencing, 신규 admission을 실행하지 않는다.

해결 방법: 첫 registry generation의 candidate로 실제 lease를 획득하고, 제거된 owner generation을 `HandleNodeDisconnect`에 전달한다. offline snapshot과 reconnect 직후 snapshot은 counter 0을 요구하고 configured capacity만 복구한다. 그 뒤 higher generation candidate를 실제 admission하여 counter 1/long 1을 확인하고 lease release 뒤 0 회복을 확인한다.

Before (`apps/edge/internal/service/status_provider_test.go:2072`):

```go
svc.queue.resources[key] = &providerResourceState{
    inFlight: 2, longInFlight: 1,
}
reg.Unregister(nodeID)
reg.Register(secondEntry)
// old 2/1 counters are expected again
```

After:

```go
firstLease := admitForGeneration(firstEntry)
reg.Unregister(nodeID)
svc.HandleNodeDisconnect(nodeID, firstEntry.ConnectionGeneration, "test disconnect")
assertOfflineCounters(0, 0)

reg.Register(secondEntry)
assertReconnectCounters(0, 0)
secondLease := admitForGeneration(secondEntry)
assertReconnectCounters(1, 1)
release(secondLease)
assertReconnectCounters(0, 0)
```

수정 파일 및 체크리스트:

- [ ] `apps/edge/internal/service/status_provider_test.go`: manual resource injection을 실제 generation-aware lease lifecycle로 교체한다.
- [ ] 구현상 필요가 증명될 때만 `apps/edge/internal/service/model_queue_snapshot.go`: old orphan counter가 current generation으로 노출되는 최소 defect를 수정한다.

테스트 작성: 기존 `TestProviderSnapshotReconnectRestoresCapacityAndAdmission`을 회귀 테스트로 유지하되 fixture와 assertion을 위 순서로 교체한다. 실제 admission과 release 결과, generation 증가, offline exact tuple, reconnect configured capacity, current lease counter만 검증한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/service/status_provider_test.go` | REVIEW_REFACTOR-1 |
| `apps/edge/internal/service/model_queue_snapshot.go` | REVIEW_REFACTOR-1, 실제 재현 시에만 |

## 최종 검증

- `gofmt -d apps/edge/internal/service/status_provider_test.go apps/edge/internal/service/model_queue_snapshot.go` → 출력 없음.
- `git diff --check -- apps/edge/internal/service/status_provider_test.go apps/edge/internal/service/model_queue_snapshot.go` → 출력 없음.
- `go test -count=1 -v ./apps/edge/internal/service -run '^TestProviderSnapshotReconnectRestoresCapacityAndAdmission$'` → authoritative disconnect 후 offline/reconnect pre-admission counter 0, 신규 admission counter 1, release 후 0으로 PASS.
- `go test -count=1 ./apps/edge/internal/service -run 'Snapshot|Offline|Reconnect'` → PASS.
- `go test -race -count=1 ./apps/edge/internal/service` → PASS, race warning 0.
- `go test -race -count=1 ./apps/edge/...` → PASS. 일시 timeout이면 실패 출력을 보존하고 해당 test 단독 재실행 뒤 전체를 순차 재실행한다.
- `bash scripts/e2e-long-context-admission-smoke.sh --preflight` → PASS 또는 rc=3이면 정확한 external endpoint blocker, 해제 조건과 scenario 미실행을 기록한다. 보조 preflight는 deterministic Go 회귀를 대체하지 않는다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우고 active pair를 유지한 채 구현 실행을 종료한다. 이 저장 상태가 runtime의 `review-ready` 신호이며 별도 handoff 파일·사용자 메시지를 만들지 않는다. 구현 에이전트는 code-review를 실행하거나 review 전용 작업을 수행하지 않고, runtime은 별도 review 에이전트 instance에 즉시 인계한다.
