<!-- task=m-provider-resource-admission-ownership-alignment/13+09_disconnect_queue_settlement plan=1 tag=REVIEW_REFACTOR -->

# Deterministic queued-disconnect surviving-provider regression

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

이 follow-up은 소스 동작을 다시 설계하는 작업이 아니라, false-positive integration test를 SDD S13의 실제 queued-disconnect fallback 분기로 고정하는 작업이다. 검증을 실행하고 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경·판단·stdout/stderr를 모두 기록한 뒤 active pair를 유지하고 구현 실행을 종료한다. 이 저장 상태와 실행 종료가 `review-ready`이며, 별도 handoff 파일·사용자 메시지를 만들지 않는다. runtime이 별도 review-agent instance를 배정하므로 구현 에이전트는 code-review 실행, 역할 전환, 판정 append, review-only 섹션 수정, log archive, `complete.log`·`USER_REVIEW.md` 작성, follow-up pair 생성·구현을 하지 않는다.

선택 Milestone의 `구현 잠금 > 결정 필요`가 실구현을 차단할 때만 review stub의 `사용자 리뷰 요청`을 정확한 연결 근거로 채운다. 구현 중 사용자에게 직접 묻거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경·secret·service blocker, 일반 범위 조정, 자동 follow-up으로 닫을 수 있는 evidence 공백은 사용자-facing 중단 사유가 아니므로 실행 근거·해제 조건·재검증 명령을 review에 기록한다.

## 배경

이전 리뷰의 신규 4개 테스트와 Edge/race 회귀는 통과했지만, surviving-provider 통합 테스트는 run-2가 disconnect 전에 이미 여유 있는 node-2로 dispatch되어 S13의 핵심 분기를 실행하지 않았다. 이 follow-up은 waiter가 먼저 queue에 들어갔음을 결정적으로 증명하고, 그 뒤 live survivor를 노출한 후 authoritative disconnect만으로 fallback하는 regression을 만든다. 새 review evidence는 실제 명령 결과와 모순 없이 기록한다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`의 형식으로 선택 Milestone `구현 잠금 > 결정 필요`만 active review stub에 기록한다. 구현 중 직접 사용자 prompt는 금지되며, 요청 타당성 판정과 실제 `USER_REVIEW.md` 작성은 code-review가 소유한다. 현재 Milestone은 `결정 필요: 없음`이며 이 follow-up은 user-review gate가 아니다.

## Archive Evidence Snapshot

- Prior task: `agent-task/m-provider-resource-admission-ownership-alignment/13+09_disconnect_queue_settlement`
- Prior plan: `agent-task/m-provider-resource-admission-ownership-alignment/13+09_disconnect_queue_settlement/plan_local_G06_0.log`
- Prior review: `agent-task/m-provider-resource-admission-ownership-alignment/13+09_disconnect_queue_settlement/code_review_local_G06_0.log`
- Verdict: `FAIL`
- Findings: Required 1 (`TestSubmitRunModelQueueSurvivingProviderFallbackAfterDisconnect`가 disconnect 전에 node-2로 즉시 dispatch되어 queued-disconnect fallback을 검증하지 않음), Suggested 0, Nit 1 (이전 review의 full-suite 설명이 자체 모순)
- Affected file: `apps/edge/internal/service/queue_dispatch_test.go`
- Existing verification: 신규 4 tests, `go test -race -count=1 -timeout 120s ./apps/edge/internal/service`, `go test -count=1 -timeout 120s ./apps/edge/...`, gofmt/diff-check는 리뷰에서 PASS. mock e2e는 local port bind blocker로 실행 완료 불가. 이 PASS들은 false-positive fallback 분기의 evidence가 아님.
- Roadmap carryover: SDD `S13` / Milestone Task `disconnect-queue-resolution`; PASS 전 deterministic queued fallback과 test/race evidence가 필요함.
- 상세 문구가 필요할 때만 위 두 archive log를 좁게 읽고, `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:
  - `disconnect-queue-resolution`: authoritative disconnect 시 fallback 또는 immediate unavailable
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `apps/edge/internal/service/queue_dispatch_test.go`: 기존 disconnect/cross-group tests와 825-1070 신규 integration tests.
- `apps/edge/internal/service/model_queue_admission_test.go`: provider-pool fixture, terminal unavailable, event-drop/no-leak tests.
- `apps/edge/internal/service/model_queue_release.go`: generation fence, `resolveAndPumpAllLocked`, `releaseNode` 전체.
- `apps/edge/internal/service/model_queue_admission.go`: live candidate, reserve, global pump, resolver tri-state 구간.
- `apps/edge/internal/service/model_queue_types.go`: queue/resource/lease type과 resource eligibility 구간.
- `apps/edge/internal/service/provider_resolution.go`, `apps/edge/internal/service/service.go`: production live resolver와 disconnect hook.
- `apps/edge/internal/node/registry.go`, `apps/edge/internal/transport/connection_handlers.go`: connection generation/ownership과 unregister-disconnect ordering.
- `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/edge-node-execution.md`, `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-contract/inner/edge-node-runtime-wire.md`, `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-ops/rules/project/domain/edge/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`
- `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: `S13` → Milestone Task `disconnect-queue-resolution`.
- Evidence Map: Edge provider resource/connectivity/queue deterministic tests, event-drop 회귀, race 검증을 `Roadmap Completion`에 남겨야 한다.
- 따라서 신규 test의 단순 PASS가 아니라 disconnect 전 queued 상태, disconnect로 인한 live re-resolution, survivor dispatch, terminal/no-leak 검증이 필수다.

### 테스트 환경 규칙

- `test_env=local`.
- 기준: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/testing-smoke.md`.
- Go cache는 `go env GOCACHE`가 반환하는 기본값을 사용하고 별도 repo-local cache를 만들지 않는다.
- 새 regression은 `-count=20` 반복과 `-race` 실행으로 ordering/flakiness를 검증하고, Edge/전체 회귀를 신선하게 실행한다.
- mock e2e가 port bind로 실행 불가하면 실제 stderr, 사용 port, 해제 조건, 동일 명령을 기록한다. 단, 이 test-only follow-up의 S13 핵심 PASS evidence는 deterministic integration/race이다.

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

- immediate terminal unavailable, queue/lease/candidate-pressure zero, event-drop 무관성: 기존 신규 tests가 커버하고 독립 실행 PASS.
- surviving provider fallback: 현 test는 run-2가 queue에 들어가지 않아 미커버. 이 follow-up의 유일 필수 regression gap.
- full project/auxiliary smoke evidence: fresh review에서 실제 exit/output과 일치하게 재기록 필요.

### 심볼 참조

- 이 follow-up은 rename/remove symbol을 만들지 않는다. 심볼 참조 변경: none.

### 분할 판단

- 선택 task: `m-provider-resource-admission-ownership-alignment/13+09_disconnect_queue_settlement` follow-up.
- 단일 작업으로 유지한다. immediate sibling set은 기존 디렉터리 하나이며 추가 split은 없다.
- Write set: `apps/edge/internal/service/queue_dispatch_test.go`, active review stub의 구현 에이전트 소유 섹션.
- Shared mutable state: provider queue, registry, goroutine completion ordering은 테스트 fixture 내에서만 조정한다. 다른 sibling의 product source를 수정하지 않아 collision이 없다.
- Dependency `09+08_terminal_unavailable_outcome`은 `agent-task/archive/2026/07/m-provider-resource-admission-ownership-alignment/09+08_terminal_unavailable_outcome/complete.log`로 충족된 상태를 prior loop가 확인했다.

### 범위 결정 근거

- 필수 변경은 `queue_dispatch_test.go`의 false-positive test로 제한한다.
- `model_queue_release.go` 및 다른 product source, offline snapshot, reconnect activation/pump, Node retry, contract/spec 갱신은 현 Required finding의 write scope가 아니므로 제외한다.
- 새 deterministic regression이 실제 source defect를 드러내면 임의로 범위를 넓히지 말고 검증 output과 release condition을 review에 기록한다.

### 최종 라우팅

- `evaluation_mode: isolated-reassessment`; 이전 lane/G/score/rationale/filename을 입력에서 제외했다.
- Build closures: scope/context/verification/evidence/ownership/decision 모두 `true`. 한 test fixture와 결정적 명령으로 완결 가능하고 사용자 결정이 없다.
- Build scores: scope_coupling=0, state_concurrency=2, blast_irreversibility=0, evidence_diagnosis=1, verification_complexity=1 → `local`, `G04`, `PLAN-local-G04.md`.
- Review closures: scope/context/verification/evidence/ownership/decision 모두 `true`. 정적 fixture 구조, 반복 test, race/Edge 명령으로 거짓 양성 제거를 판정할 수 있다.
- Review scores: scope_coupling=0, state_concurrency=2, blast_irreversibility=0, evidence_diagnosis=1, verification_complexity=1 → `local`, `G04`, `CODE_REVIEW-local-G04.md`.

## 구현 체크리스트

- [ ] `TestSubmitRunModelQueueSurvivingProviderFallbackAfterDisconnect`가 run-2의 queued 상태를 bounded state synchronization으로 먼저 증명하고, queueing 후 survivor를 live resolver에 노출한 뒤 authoritative node-1 disconnect만으로 node-2 dispatch되는지 검증하도록 fixture를 고친다.
- [ ] disconnect 전 submit goroutine이 완료되지 않았음, disconnect 후 bounded timeout 안에 정확히 한 번 완료됨, dispatch node/provider, 마지막 queue/lease/provider counter 회복을 assertion하고 근거 없는 fixed sleep에 의존하지 않는다.
- [ ] 대상 반복 test, 신규 4-test race, Edge/전체 회귀, mock e2e 결과 또는 명시 blocker를 실제 stdout/stderr와 함께 기록하고 서로 모순된 PASS/실패 설명을 남기지 않는다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우고 active pair를 유지한 채 구현 실행을 종료한다. 이 저장 상태가 runtime의 review-ready 신호이며 별도 handoff 파일·사용자 메시지를 만들지 않는다. 구현 에이전트는 code-review를 실행하거나 review 전용 작업을 수행하지 않는다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REFACTOR-1] Exercise the real queued-disconnect fallback branch

문제: `apps/edge/internal/service/queue_dispatch_test.go:906-940`은 두 살아 있는 capacity-1 provider 중 node-1만 채운 뒤 run-2를 보내므로 scheduler가 disconnect 전에 node-2를 즉시 선택한다. 결과적으로 test name/comment와 달리 live re-resolution fallback을 검증하지 않는다.

해결 방법: 초기 registry에는 node-1만 두고 store/catalog에는 node-2 provider를 유지한다. node-1 holder 후 run-2를 보내 queue pressure 또는 동등한 bounded 상태 assertion으로 waiter가 대기 중임을 확인한다. 그 뒤 node-2를 registry에 등록하되 별도 queue pump를 일으키지 않고, node-1을 ownership/generation에 맞게 unregister/disconnect하여 그 전이 유일한 wake-up trigger임을 고정한다.

```go
// Before: node-2 is live before run-2, so run-2 never queues.
reg.Register(node1)
reg.Register(node2)
go submitRun2()
time.Sleep(30 * time.Millisecond)
svc.HandleNodeDisconnect(node1ID, 0, "disconnected")

// After: prove queued state, then expose the survivor and disconnect node-1.
reg.Register(node1)
go submitRun2()
waitForQueuedPressure(t, svc, node1ID, provider1ID)
assertNotCompleted(t, run2Done)
reg.Register(node2)
reg.Unregister(node1ID)
svc.HandleNodeDisconnect(node1ID, node1Generation, "disconnected")
assertDispatchedWithin(t, run2Done, node2ID)
```

수정 파일 및 체크리스트:

- [ ] `apps/edge/internal/service/queue_dispatch_test.go`: 실제 queued-disconnect fallback fixture, bounded synchronization, pre/post assertions.
- [ ] 새 helper가 필요하면 같은 test file 내의 구체적 이름 helper로만 두고 product API를 추가하지 않는다.

테스트 작성: 기존 `TestSubmitRunModelQueueSurvivingProviderFallbackAfterDisconnect`를 수정한다. assertion goal은 (1) disconnect 전 queued, (2) survivor 등록만으로는 premature completion 없음, (3) disconnect만으로 node-2 dispatch, (4) bounded completion, (5) 종료 후 counter/queue 회복이다.

## 수정 파일 요약

| 파일 | Item |
|------|------|
| `apps/edge/internal/service/queue_dispatch_test.go` | REVIEW_REFACTOR-1 |

## 최종 검증

- `gofmt -d apps/edge/internal/service/queue_dispatch_test.go` → 출력 없음.
- `go test -count=20 -timeout 120s -run '^TestSubmitRunModelQueueSurvivingProviderFallbackAfterDisconnect$' -v ./apps/edge/internal/service` → 모든 반복 PASS, disconnect 전 queued assertion과 disconnect 후 node-2 dispatch assertion 통과.
- `go test -race -count=1 -timeout 120s -run '^(TestProviderPoolDisconnectAuthoritativeSettlementNoLeak|TestProviderPoolLastProviderDisconnectTerminalSettlement|TestSubmitRunModelQueueSurvivingProviderFallbackAfterDisconnect|TestSubmitRunModelQueueLastProviderDisconnectReturnsUnavailable)$' ./apps/edge/internal/service` → PASS, race warning 없음.
- `go test -count=1 -timeout 120s ./apps/edge/...` → PASS.
- `go test -count=1 -timeout 180s ./...` → PASS. 실패하면 실제 failing package/test/output을 모순 없이 기록.
- `IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh` → auxiliary smoke PASS 또는 실제 port/process blocker, 결정적 해제 조건, 동일 재검증 명령 기록.
- `git diff --check -- apps/edge/internal/service/queue_dispatch_test.go` → 출력 없음.

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