<!-- task=m-provider-availability-capacity-queue-foundation/05+04_queue_observe_snapshot plan=0 tag=API -->

# Plan - Queue Observation Snapshot

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

구현의 마지막 단계는 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이다. 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 둔 채 리뷰 준비 완료를 보고한다. 사용자 결정, 사용자 소유 외부 환경, scope conflict 없이는 사용자에게 직접 묻지 말고, 차단 시 review stub의 `사용자 리뷰 요청` 섹션에 근거를 남긴다. `USER_REVIEW.md`, `complete.log`, log/archive 이동은 code-review 전용이다.

## 배경

`ProviderSnapshot`에는 이미 `capacity`, `in_flight`, `queued`가 있지만 Node capabilities 응답은 `queued`를 0으로 고정한다. FIFO queue가 들어간 뒤 Edge가 capacity-aware routing 입력으로 쓸 수 있으려면 실제 queued count를 snapshot에 반영해야 한다. 이 계획은 queue 본체 완료 후 관측 표면만 좁게 정리한다.

## 사용자 리뷰 요청 흐름

구현 중 사용자-only blocker는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 직접 질문, 채팅 선택지 제시, `request_user_input` 호출을 하지 않는다. code-review가 요청 타당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/provider-availability-capacity-queue-foundation.md`
- Task ids:
  - `queue-observe`: Edge가 보는 snapshot에 `capacity`, `in_flight`, `queued`가 포함되어 이후 capacity-aware routing의 입력으로 사용할 수 있다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/node-smoke.md`
- `agent-test/local/edge-smoke.md`
- `apps/node/internal/node/node.go`
- `apps/node/internal/node/run_manager.go`
- `apps/node/internal/node/node_test.go`
- `apps/edge/internal/service/node_command.go`
- `apps/edge/internal/service/status_provider.go`
- `apps/edge/internal/service/service_test.go`
- `apps/edge/internal/controlplane/connector.go`
- `apps/edge/internal/controlplane/connector_test.go`
- `proto/iop/runtime.proto`
- `proto/iop/control.proto`

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md` 존재 및 읽음.
- 적용 profile: `agent-test/local/node-smoke.md`, `agent-test/local/edge-smoke.md`.
- Node command response 변경 검증은 `go test -count=1 ./apps/node/internal/node`.
- Edge 보존 경로 검증은 `go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane`.

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

- Node capabilities currently asserts queued `0`; FIFO queue 대기 중 queued `>0` assertion 필요.
- Edge service/control-plane tests already preserve provider snapshot values; queued non-zero fixture로 회귀 보강이 필요하다.

### 심볼 참조

- renamed/removed symbols: none.
- changed fields: `ProviderSnapshot.Queued`, command result `"queued"`.
- call sites found by `rg --sort path -n "ProviderSnapshots|queued|ProviderSnapshot" apps/node apps/edge proto/iop`.

### 분할 판단

- split decision policy 평가 완료.
- 이 subtask는 `05+04_queue_observe_snapshot`; predecessor `04+03_fifo_admission_queue` PASS `complete.log`가 필요하다.
- active predecessor complete.log는 현재 없음. 구현 시작 전 active/archive predecessor completion을 확인한다.

### 범위 결정 근거

- 포함: Node capabilities queued count, Edge snapshot preservation tests.
- 제외: FIFO admission/release/reject 본체. predecessor 04에서 완료되어야 한다.
- 제외: capacity-aware routing. 이 milestone의 범위 제외에 해당한다.

### 빌드 등급

- `local-G05`: predecessor queue manager 위에 관측 값을 연결하는 좁은 change이며 focused tests로 검증 가능하다.

## 구현 체크리스트

- [ ] predecessor `04+03_fifo_admission_queue`의 `complete.log`가 active 또는 archive에 있는지 확인한다.
- [ ] Node capabilities command가 admission manager의 actual `in_flight`와 `queued` 값을 읽는다.
- [ ] `NodeCommandResponse.Result["queued"]`와 `ProviderSnapshot.Queued`에 같은 queued count를 넣는다.
- [ ] Edge service/control-plane preservation tests에 queued non-zero fixture를 추가한다.
- [ ] 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- `05+04_queue_observe_snapshot`는 디렉터리명 기준으로 `04+03_fifo_admission_queue`의 PASS `complete.log`가 선행 조건이다.

### [API-1] Node Capabilities Queued Count

문제: [node.go](/config/workspace/iop/apps/node/internal/node/node.go:344)는 capabilities result의 `queued`를 `"0"`으로 고정하고 [node.go](/config/workspace/iop/apps/node/internal/node/node.go:358)의 `ProviderSnapshot.Queued`도 0으로 고정한다.

해결 방법:

Before:
```go
// apps/node/internal/node/node.go:350
"capacity":        strconv.Itoa(caps.MaxConcurrency),
"in_flight":       strconv.Itoa(inFlight),
"queued":          "0",
```

After:
```go
stats := n.admissionStats(req.GetAdapter())
"capacity":  strconv.Itoa(stats.Capacity),
"in_flight": strconv.Itoa(stats.InFlight),
"queued":    strconv.Itoa(stats.Queued),
```

수정 파일 및 체크리스트:
- [ ] `apps/node/internal/node/node.go`: admission stats를 capabilities response에 반영.
- [ ] `apps/node/internal/node/run_manager.go`: queued count accessor가 없다면 추가.

테스트 작성:
- [ ] `apps/node/internal/node/node_test.go`: capacity full 상태에서 request가 queued일 때 capabilities `queued=1`, snapshot `Queued=1` 검증.

중간 검증:
```bash
go test -count=1 ./apps/node/internal/node -run 'TestOnCommandRequest_Capabilities'
```
예상: PASS.

### [API-2] Edge Snapshot Preservation

문제: Edge는 provider snapshots를 보존하지만 queued non-zero fixture가 약하면 Node 관측 값이 Edge/Control Plane 표면까지 유지되는지 회귀 검증이 약하다.

해결 방법:

Before:
```go
// apps/edge/internal/service/service_test.go fixture
Queued: 0,
```

After:
```go
Queued: 3,
```

수정 파일 및 체크리스트:
- [ ] `apps/edge/internal/service/service_test.go`: queued non-zero preservation assertion.
- [ ] `apps/edge/internal/controlplane/connector_test.go`: queued non-zero preservation assertion.
- [ ] 소스 production code는 기존 mapper가 값을 보존하면 변경하지 않는다.

테스트 작성: 위 edge tests fixture/assertion 갱신.

중간 검증:
```bash
go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane
```
예상: PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/node/node.go` | API-1 |
| `apps/node/internal/node/run_manager.go` | API-1 |
| `apps/node/internal/node/node_test.go` | API-1 |
| `apps/edge/internal/service/service_test.go` | API-2 |
| `apps/edge/internal/controlplane/connector_test.go` | API-2 |

## 최종 검증

```bash
go test -count=1 ./apps/node/internal/node
go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/controlplane
go test -count=1 ./apps/node/... ./apps/edge/...
```

예상: PASS. Go test cache output은 허용하지 않는다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
