ㄹ<!-- task=m-provider-resource-admission-ownership-alignment/16_capacity_smoke plan=0 tag=TEST -->

# Provider capacity smoke

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

구현 모델은 Plan/Review 모델과 다른 identity여야 한다. 구현 소유 review stub 섹션을 채우고 `review-ready`를 보고한 뒤 종료한다. code-review를 실행하거나 다음 에이전트를 시작하지 않는다.

## 역할 모델

- Plan: `gpt-5.6-terra`
- Implementation/Review: [CODE_REVIEW-local-G08.md](CODE_REVIEW-local-G08.md)의 runtime ledger가 최초 역할 시작 시 기록한다. 세 역할 identity는 모두 달라야 한다.

## 배경

남은 `capacity-smoke`는 shared capacity-1 provider를 가리키는 `ornith:35b`와 `ornith-fast`의 동시 호출, backend peak=1, 종료 뒤 normal/long counter=0을 full-cycle로 남겨야 한다. 기존 long-context smoke는 live endpoint/token/status를 전제하고 단일 model 중심이라 S11의 deterministic local 증명이 아니다. Control Plane HTTP status view도 wire의 long counter를 JSON으로 옮기지 않아 final-zero assertion이 불가능하다.

## 사용자 리뷰 요청 흐름

선택 Milestone의 `구현 잠금 > 결정 필요`만 [사용자 리뷰 요청](CODE_REVIEW-local-G08.md#사용자-리뷰-요청)에 기록한다. 환경·도구·process blocker는 검증 evidence와 재개 조건으로 남기며 `USER_REVIEW.md`는 code-review 소유다.

## 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:
  - `capacity-smoke`: two model key concurrent shared-provider smoke, backend peak 1, waiting/fallback, and final counter recovery.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`, Phase/Milestone, approved SDD (`S11`, Evidence Map S11).
- `agent-test/local/rules.md`, `testing-smoke.md`, `edge-smoke.md`, `node-smoke.md`, `control-plane-smoke.md`; testing/edge/control-plane domain rules.
- matching contract index plus Edge-Node, Control Plane-Edge, Edge config/refresh, OpenAI-compatible contracts.
- `scripts/e2e-long-context-admission-smoke.sh` (전체), `scripts/e2e-openai-vllm.sh` (전체), `scripts/e2e-openai-lemonade.sh` (targeted fake provider/config lifecycle), `scripts/e2e-smoke.sh` (targeted startup), `scripts/dev/edge.sh`, `scripts/dev/node.sh`, `Makefile`.
- `apps/control-plane/cmd/control-plane/http_views.go` (`providerSnapshotView`/mapping), `edge_registry_handler_test.go` (`TestEdgeRegistryHTTPHandlersGetLiveStatus`), `main.go`, `server.go`, `http_edge_handlers.go`.
- `apps/edge/internal/service/status_provider.go`, `apps/edge/internal/controlplane/connector.go`, `packages/go/config/edge_types.go`, `provider_types.go`, `proto/iop/runtime.proto` long provider fields, and targeted `reconnect_readiness_integration_test.go` local config/snapshot helpers.

### SDD 기준

- SDD `agent-roadmap/sdd/operational-observability-provider-management/provider-resource-admission-ownership-alignment/SDD.md`는 `[승인됨]`, SDD 잠금 `해제`다.
- S11 / `capacity-smoke`의 Evidence Map은 local 또는 dev-corp cross-model smoke log에 backend peak 1과 종료 후 counter 0을 기록하도록 요구한다. 따라서 two-alias temporary config, deterministic queued observation, backend stats, Control Plane normal/long final-zero assertion을 한 closure로 둔다.

### 테스트 환경 규칙

- `test_env=local`; rules/profile은 `agent-test/local/rules.md`, testing/edge/node/control-plane smoke다.
- build/review는 checkout에서 `curl`, `jq`, `go`, loopback process/port preflight를 기록한다. 외부 endpoint와 `IOP_LONG_SMOKE_TOKEN`은 local harness 입력이 아니다.
- static fallback은 fake provider와 temporary Edge/Node process precedent 및 Control Plane `/edges/{id}/status` handler다. 현재 process/port가 살아 있다고 가정하지 않는다.

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

- Control Plane JSON view의 long capacity/in-flight/queued regression이 없다. handler fixture의 typed/raw JSON assertion을 추가한다.
- Existing live smoke는 two-alias capacity-1 deterministic queue/peak/final-zero를 다루지 않는다. 새 script가 fake backend + Control Plane + Edge + Node full-cycle regression이 된다.
- Root가 작업 중인 `apps/edge/internal/service/model_queue_admission_test.go`와 `long_context_queue_test.go`는 읽기/수정 범위에서 제외한다.

### 심볼 참조

없음. rename/remove하지 않는다.

### 분할 판단

- candidate `capacity-smoke`는 `16_capacity_smoke` 단일 sibling으로 유지한다. write set은 `apps/control-plane/cmd/control-plane/http_views.go`, `apps/control-plane/cmd/control-plane/edge_registry_handler_test.go`, 새 `scripts/e2e-provider-capacity-smoke.sh`다.
- shared mutable state는 temporary loopback process/port/output뿐이다. status JSON change가 script의 long-zero proof를 가능하게 하므로 분리하면 S11 closure가 깨진다. predecessor/dependency 없음.
- archive sibling basename은 `15+14_controlplane_connected_mapping`까지이고 active sibling은 없어 index `16`을 할당했다.

### 범위 결정 근거

- `scripts/e2e-long-context-admission-smoke.sh`는 external live smoke 역할을 유지한다. default config, contract/spec/roadmap, Edge admission source, root의 dirty contract/spec/Milestone/Edge service tests는 수정하지 않는다.
- Control Plane change는 existing wire field의 read-only rendering뿐이며 Node scheduling, Edge state ownership, proto/config contract 의미를 바꾸지 않는다.

### 최종 라우팅

- `evaluation_mode: first-pass`.
- build/review 모두 scope/context/verification/evidence/ownership/decision closure `true`; score `scope_coupling=2`, `state_concurrency=2`, `blast_irreversibility=1`, `evidence_diagnosis=1`, `verification_complexity=2`; lane `local`, grade `G08`.
- canonical output: `PLAN-local-G08.md`, `CODE_REVIEW-local-G08.md`.

## 구현 체크리스트

- [ ] [TEST-1] Control Plane status JSON에 existing long provider capacity/in-flight/queued wire values를 loss 없이 전달하고 typed/raw handler regression으로 고정한다.
- [ ] [TEST-2] temporary fake OpenAI-compatible backend, Control Plane, Edge, Node config의 local smoke에서 `ornith:35b`/`ornith-fast` 동시 호출, queue/fallback, peak=1, final normal/long zero를 evidence로 기록한다.
- [ ] [TEST-3] `gofmt -d apps/control-plane/cmd/control-plane/http_views.go apps/control-plane/cmd/control-plane/edge_registry_handler_test.go`, `go test -count=1 ./apps/control-plane/...`, `go test -count=1 ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/service ./apps/edge/internal/bootstrap`, `go test -count=1 ./apps/node/internal/transport ./apps/node/internal/bootstrap`, `bash -n scripts/e2e-provider-capacity-smoke.sh`, `./scripts/e2e-provider-capacity-smoke.sh`, `IOP_E2E_BIND_TIMEOUT=60 IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh` actual output을 stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채우고 review-ready를 보고한 뒤 현재 실행을 종료한다. code-review를 실행하거나 다음 에이전트를 시작하지 않는다.

### [TEST-1] Long provider counters in Control Plane status

**문제**

`apps/control-plane/cmd/control-plane/http_views.go:147-161`와 `:195-218`은 normal fields만 JSON view에 복사한다. `proto/iop/runtime.proto:201-203` long fields를 local status poll에서 확인할 수 없다.

**해결 방법**

`providerSnapshotView`에 `long_context_capacity`, `long_in_flight`, `long_queued` int32 fields를 추가하고 mapping이 corresponding getters를 복사한다. `TestEdgeRegistryHTTPHandlersGetLiveStatus` fixture에 nonzero long values를 넣어 typed view와 raw JSON을 확인하며 no-secret assertion은 유지한다.

Before (`apps/control-plane/cmd/control-plane/http_views.go:147-161`):

```go
InFlight int32
Queued   int32
```

After:

```go
InFlight            int32
Queued              int32
LongContextCapacity int32
LongInFlight        int32
LongQueued          int32
```

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

- [ ] `apps/control-plane/cmd/control-plane/http_views.go`: view fields와 getter mapping만 추가한다.
- [ ] `apps/control-plane/cmd/control-plane/edge_registry_handler_test.go`: fixture, typed-view, raw JSON assertions을 추가한다.

**테스트 작성**

- 작성: existing `TestEdgeRegistryHTTPHandlersGetLiveStatus`가 `ProviderSnapshot` → HTTP view → JSON long values 보존을 확인한다.

### [TEST-2] Deterministic local two-alias capacity smoke

**문제**

`scripts/e2e-long-context-admission-smoke.sh:1-584`는 live endpoint/token/status URL과 one `MODEL` scenario다. S11 two-alias shared capacity-1 proof를 no-secret local default로 만들 수 없다.

**해결 방법**

새 script가 `mktemp -d` 아래 fake provider, temporary Control Plane/Edge/Node YAML, logs/evidence를 만들고 cleanup trap으로 process tree를 정리한다. fake backend는 첫 request를 release sentinel까지 hold하고 stats endpoint에서 active/peak를 제공한다. `models[]`의 `ornith:35b`와 `ornith-fast`는 하나의 capacity-1, long-context-capacity-1 resource로 map한다. 두 curl을 병렬 시작해 status `in_flight=1`/`queued>=1`을 확인한 뒤 release하고, two 200, backend peak=1/active=0, every provider normal/long final zero를 jq로 assert한다.

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

- [ ] 새 `scripts/e2e-provider-capacity-smoke.sh`: loopback-only config, deterministic readiness/timeouts, concurrent requests, status/stat assertions, temp evidence path, secret-free output, safe cleanup.
- [ ] `scripts/e2e-long-context-admission-smoke.sh`, default config, contracts/spec/roadmap, Edge admission implementation은 수정하지 않는다.

**테스트 작성**

- 작성: new script가 regression/full-cycle harness다. second backend concurrent request, queue observation 부재, non-200 alias response, unavailable status, final normal/long nonzero에서 fail해야 한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `apps/control-plane/cmd/control-plane/http_views.go` | TEST-1 |
| `apps/control-plane/cmd/control-plane/edge_registry_handler_test.go` | TEST-1 |
| `scripts/e2e-provider-capacity-smoke.sh` | TEST-2 |

## 최종 검증

```bash
gofmt -d apps/control-plane/cmd/control-plane/http_views.go apps/control-plane/cmd/control-plane/edge_registry_handler_test.go
go test -count=1 ./apps/control-plane/...
go test -count=1 ./apps/edge/internal/node ./apps/edge/internal/transport ./apps/edge/internal/service ./apps/edge/internal/bootstrap
go test -count=1 ./apps/node/internal/transport ./apps/node/internal/bootstrap
bash -n scripts/e2e-provider-capacity-smoke.sh
./scripts/e2e-provider-capacity-smoke.sh
IOP_E2E_BIND_TIMEOUT=60 IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh
```

Expected: format diff 없음; tests/smokes exit 0; evidence shows two aliases, queue/fallback, backend peak 1, all provider normal/long counters 0. Go test cache is not acceptable. External live preflight is not a S11 pass prerequisite.

검증 결과를 review stub에 기록하고 review-ready를 보고한 뒤 현재 실행을 종료한다. code-review를 실행하거나 다음 에이전트를 시작하지 않는다.
