<!-- task=m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke plan=1 tag=REVIEW_CAPACITY_SMOKE -->

# Plan - REVIEW_CAPACITY_SMOKE

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

이 task는 이전 CAPACITY_SMOKE 리뷰의 FAIL 후속이다. `scripts/e2e-long-context-admission-smoke.sh`의 smoke 판정이 실제 실패를 non-zero로 전파하도록 고치고, 고친 스크립트로 dev provider pool evidence를 다시 수집한다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 stdout/stderr와 evidence 경로를 채운 뒤 active 파일을 남긴 채 review ready로 멈춘다. finalization, log rename, complete.log, archive move는 code-review 전용이다.

Milestone lock 결정이 아니라 환경/secret/service 준비, smoke 실패, evidence 공백은 사용자 리뷰 요청이 아니다. 구현 중 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 말고, 필요한 blocker는 review stub의 검증 결과에 정확한 명령과 출력으로 기록한다.

## 배경

이전 구현은 long-context admission dev smoke 스크립트와 문서를 추가했지만, 스크립트가 HTTP 실패, config check 실패, provider identity 누락, peak/queue 미달을 exit status에 반영하지 않는다. SDD S09의 완료 evidence는 dev smoke script/output과 final recovery snapshot이므로, smoke command 자체가 판정 기준을 강제하고 그 실제 출력이 review stub에 남아야 한다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 selected Milestone `구현 잠금 > 결정 필요` 항목만 active review stub에 기록한다. 이번 follow-up의 예상 blocker는 script/test/evidence 문제이며 사용자 리뷰 요청이 아니다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
- Task ids:
  - `capacity-smoke`: dev 환경 normal 10-way, mixed long/normal, all-long-slot-full queue 시나리오와 최종 회복 근거
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Current archived plan: `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/plan_cloud_G08_0.log`
- Current archived review: `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/code_review_cloud_G08_0.log`
- Verdict: FAIL
- Required summary:
  - `scripts/e2e-long-context-admission-smoke.sh` logs HTTP success counts but does not fail scenarios when counts are short.
  - preflight does not fail when `edge config check` fails or required provider identities are missing.
  - scenario peak/queue criteria from `agent-test/dev/long-context-admission-smoke.md` are recorded but not asserted.
  - post-fix live evidence in the review log is summarized, not preserved as actual stdout/stderr or exact snapshot/log paths.
- Affected files:
  - `scripts/e2e-long-context-admission-smoke.sh`
  - `agent-test/dev/long-context-admission-smoke.md`
  - `agent-test/dev/edge-smoke.md`
  - `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/CODE_REVIEW-cloud-G08.md`
- Verification evidence from review:
  - `bash -n scripts/e2e-long-context-admission-smoke.sh`: PASS.
  - `go run ./apps/edge/cmd/edge config check --config configs/edge.yaml`: PASS.
  - Local mock reproduced the exit bug: `all-long-slot-full` returned `rc=0` with `http_200=0/6`, `peak_in_flight=0`, `peak_queued=0`.
  - Local mock reproduced preflight bug: missing config returned `preflight_bad_config_rc=0`.
- Allowed narrow reread:
  - `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/code_review_cloud_G08_0.log`
  - `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/plan_cloud_G08_0.log`

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.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/_templates/implementation-user-review-request-section.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/operational-observability-provider-management/milestones/model-group-long-context-admission.md`
- `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
- `agent-test/local/rules.md`
- `agent-test/dev/rules.md`
- `agent-test/dev/edge-smoke.md`
- `agent-test/dev/node-smoke.md`
- `agent-test/dev/inventory.yaml`
- `agent-test/dev/long-context-admission-smoke.md`
- `scripts/e2e-long-context-admission-smoke.sh`
- `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/PLAN-cloud-G08.md`
- `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/CODE_REVIEW-cloud-G08.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/model-group-long-context-admission/SDD.md`
- 상태: `[승인됨]`, SDD 잠금 `해제`
- 대상 Acceptance: S09 / Milestone Task `capacity-smoke`
- Evidence Map: dev smoke script/output and final recovery snapshot
- 적용: smoke script는 normal 10-way, mixed, all-long-slot-full의 HTTP success, peak/queue, recovery criteria를 command exit status로 강제해야 한다. review evidence는 실제 stdout/stderr 또는 exact output file/status snapshot/Edge log path를 남겨야 한다.

### 테스트 환경 규칙

- test_env: local 기본 규칙을 읽었고, dev provider pool live evidence 때문에 `agent-test/dev/rules.md`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`, `agent-test/dev/inventory.yaml`, `agent-test/dev/long-context-admission-smoke.md`를 적용한다.
- 로컬 quick check:
  - `bash -n scripts/e2e-long-context-admission-smoke.sh`
  - `go run ./apps/edge/cmd/edge config check --config configs/edge.yaml`
- dev live smoke:
  - runner: `ssh toki@toki-labs.com`
  - repo root: `/Users/toki/agent-work/iop-dev`
  - Edge config: `build/dev-runtime/edge.yaml`
  - Edge base URL: runner-local `http://127.0.0.1:18083/v1`
  - Control Plane status: `http://127.0.0.1:18001/edges/edge-toki-labs-dev/status`
  - providers: `gx10-vllm`, `onexplayer-lemonade`, `mac-mlx-vllm`
  - expected normal capacity total: `9`
  - expected long slot total: `4`
  - token/secret 원문은 명령 출력과 tracked 파일에 기록하지 않는다.

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

- 기존 unit tests는 shell smoke script의 exit contract를 검증하지 않는다.
- 이 follow-up은 deterministic local mock regression으로 bad config, HTTP failure, zero peak/queue가 non-zero로 실패하는지 검증한다.
- live provider pool behavior는 unit test로 대체하지 않고 dev smoke output으로 검증한다.

### 심볼 참조

- renamed/removed symbol: none.

### 분할 판단

- 기존 split task `06+01,02,03,04,05_capacity_smoke` 안의 review follow-up이다.
- 새 subtask로 분리하지 않는다. script exit contract와 evidence rerun은 같은 smoke completion evidence를 닫는 단일 보정 흐름이며, 따로 PASS시킬 중간 Milestone Task가 없다.

### 범위 결정 근거

- Edge admission algorithm, scheduler, Control Plane status view 확장은 범위 제외다. 이 follow-up은 smoke script와 evidence capture만 고친다.
- 배포 dev config의 `long_context_threshold_tokens=30000` 정책 영속화와 SDD 갱신은 범위 제외다.
- 기존 tracked `configs/edge.yaml` 기본값을 dev 검증용 임시값으로 바꾸지 않는다.

### 빌드 등급

- `cloud-G08`: shell/CLI workflow, exit-status contract, live dev provider pool evidence, long-running external runtime smoke가 중심이다.

## 구현 체크리스트

- [ ] `scripts/e2e-long-context-admission-smoke.sh`가 preflight config/provider/capacity 실패와 request HTTP 실패를 non-zero exit으로 전파하도록 수정한다.
- [ ] `scripts/e2e-long-context-admission-smoke.sh`가 `normal-10`, `mixed`, `all-long-slot-full`의 documented peak/queue/recovery criteria를 자동 assert하도록 수정한다.
- [ ] deterministic local mock regression으로 bad config, HTTP failure, zero peak/queue가 실패로 판정되는지 검증한다.
- [ ] live dev provider pool에서 preflight와 세 scenario를 rerun하고 actual stdout/stderr 또는 exact output file/status snapshot/Edge log evidence를 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_CAPACITY_SMOKE-1] Smoke script exit contract

### 문제

- `scripts/e2e-long-context-admission-smoke.sh:286` counts HTTP 200 responses but does not return failure when the count is short.
- `scripts/e2e-long-context-admission-smoke.sh:315` prints `config check FAILED` but leaves `ok=1`.
- `scripts/e2e-long-context-admission-smoke.sh:340` logs missing providers as `WARN` and still allows `PREFLIGHT OK`.
- `scripts/e2e-long-context-admission-smoke.sh:373` through `scripts/e2e-long-context-admission-smoke.sh:406` do not enforce the documented peak/queue criteria.

### 해결 방법

- Make request summary helpers return success counts and fail on short counts.
- Capture `edge config check` status and set preflight `ok=0` on failure.
- In preflight, require each expected provider id to exist, require healthy status, and require summed capacity to match `NORMAL_CAPACITY_TOTAL`.
- Make `poll_peaks` output parseable key/value fields and add scenario assertions:
  - `normal-10`: HTTP 200 `10/10`, peak `in_flight >= 9`, peak `queued >= 1`, final recovery 0/0.
  - `mixed`: long HTTP 200 `4/4`, normal HTTP 200 `6/6`, final recovery 0/0. If long fields are visible, require peak `long_in_flight >= 4`; if not visible, leave Edge log evidence capture as review evidence.
  - `all-long-slot-full`: long HTTP 200 `6/6`, peak `in_flight <= 4` for the long-only burst, peak `queued >= 1`, final recovery 0/0. If long queue fields are visible, require `peak_long_queued >= 1`; otherwise require Edge log evidence in the review output.

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

- [ ] `scripts/e2e-long-context-admission-smoke.sh`
- [ ] `agent-test/dev/long-context-admission-smoke.md` only if command/criteria wording changes.

### 테스트 작성

- 새 repo test file은 만들지 않는다. 이 task의 regression은 `/tmp` mock server를 쓰는 deterministic command로 검증한다.
- The mock must prove that a bad config and HTTP 500/zero peak no longer exit 0.

### 중간 검증

```bash
bash -n scripts/e2e-long-context-admission-smoke.sh
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
```

## [REVIEW_CAPACITY_SMOKE-2] Trusted S09 evidence rerun

### 문제

- `code_review_cloud_G08_0.log` records live post-fix smoke as summaries. SDD S09 requires script/output and final recovery snapshot evidence, and the plan required stdout/stderr plus snapshot paths.

### 해결 방법

- After REVIEW_CAPACITY_SMOKE-1, rerun preflight and all three scenarios against the dev runner.
- Save full command stdout/stderr to out-dir or paste concise complete output. If output is too long, record exact files generated by the command.
- Record exact status snapshot paths and Edge log evidence path/snippets for long classification and `queue_reason="long_context_capacity_full"`.

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

- [ ] `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/CODE_REVIEW-cloud-G08.md`

### 테스트 작성

- No additional test file. The live smoke output is the required S09 evidence.

### 중간 검증

```bash
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git rev-parse HEAD && git status --short'
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `scripts/e2e-long-context-admission-smoke.sh` | REVIEW_CAPACITY_SMOKE-1 |
| `agent-test/dev/long-context-admission-smoke.md` | REVIEW_CAPACITY_SMOKE-1 |
| `agent-task/m-model-group-long-context-admission/06+01,02,03,04,05_capacity_smoke/CODE_REVIEW-cloud-G08.md` | REVIEW_CAPACITY_SMOKE-2 |

## 최종 검증

```bash
git rev-parse HEAD
git status --short
go run ./apps/edge/cmd/edge config check --config configs/edge.yaml
bash -n scripts/e2e-long-context-admission-smoke.sh
```

Run this deterministic local mock regression from `/tmp` and record the output. The two expected results are: bad config preflight exits non-zero, and all-long-slot-full with HTTP 500 plus zero peaks exits non-zero.

```bash
tmpdir=$(mktemp -d); port=18997; PORT=$port python3 - <<'PY' &
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, os
class H(BaseHTTPRequestHandler):
    def log_message(self, fmt, *args): pass
    def do_GET(self):
        if self.path == "/v1/models":
            body = {"data":[{"id":"qwen3.6:35b"}]}
        elif self.path == "/status":
            body = {"provider_snapshots":[
                {"id":"gx10-vllm","capacity":4,"in_flight":0,"queued":0,"health":"healthy"},
                {"id":"onexplayer-lemonade","capacity":3,"in_flight":0,"queued":0,"health":"healthy"},
                {"id":"mac-mlx-vllm","capacity":2,"in_flight":0,"queued":0,"health":"healthy"}]}
        else:
            self.send_response(404); self.end_headers(); return
        data = json.dumps(body).encode()
        self.send_response(200); self.send_header("Content-Type","application/json"); self.send_header("Content-Length",str(len(data))); self.end_headers(); self.wfile.write(data)
    def do_POST(self):
        self.rfile.read(int(self.headers.get("content-length","0") or 0))
        self.send_response(500); self.end_headers(); self.wfile.write(b"fail")
HTTPServer(("127.0.0.1", int(os.environ["PORT"])), H).serve_forever()
PY
pid=$!; trap 'kill $pid 2>/dev/null || true; rm -rf "$tmpdir"' EXIT; sleep 1
if IOP_LONG_SMOKE_BASE_URL="http://127.0.0.1:$port/v1" IOP_LONG_SMOKE_STATUS_URL="http://127.0.0.1:$port/status" bash scripts/e2e-long-context-admission-smoke.sh --preflight --config "$tmpdir/missing.yaml" --out-dir "$tmpdir/out-preflight"; then echo "bad config unexpectedly passed"; exit 1; else echo "bad config failed as expected"; fi
if IOP_LONG_SMOKE_BASE_URL="http://127.0.0.1:$port/v1" IOP_LONG_SMOKE_STATUS_URL="http://127.0.0.1:$port/status" IOP_LONG_SMOKE_LONG_CHARS=100 IOP_LONG_SMOKE_POLL_SECS=2 bash scripts/e2e-long-context-admission-smoke.sh --scenario all-long-slot-full --out-dir "$tmpdir/out-scenario"; then echo "http failure unexpectedly passed"; exit 1; else echo "http failure failed as expected"; fi
```

Then rerun live dev smoke on the remote runner without printing token values:

```bash
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_LONG_SMOKE_TOKEN:-}" && IOP_LONG_SMOKE_BASE_URL=http://127.0.0.1:18083/v1 IOP_LONG_SMOKE_LONG_CHARS=384000 bash scripts/e2e-long-context-admission-smoke.sh --preflight --config build/dev-runtime/edge.yaml --out-dir /tmp/iop-long-admission-smoke'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_LONG_SMOKE_TOKEN:-}" && IOP_LONG_SMOKE_BASE_URL=http://127.0.0.1:18083/v1 IOP_LONG_SMOKE_LONG_CHARS=384000 bash scripts/e2e-long-context-admission-smoke.sh --scenario normal-10 --config build/dev-runtime/edge.yaml --out-dir /tmp/iop-long-admission-smoke'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_LONG_SMOKE_TOKEN:-}" && IOP_LONG_SMOKE_BASE_URL=http://127.0.0.1:18083/v1 IOP_LONG_SMOKE_LONG_CHARS=384000 bash scripts/e2e-long-context-admission-smoke.sh --scenario mixed --config build/dev-runtime/edge.yaml --out-dir /tmp/iop-long-admission-smoke'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_LONG_SMOKE_TOKEN:-}" && IOP_LONG_SMOKE_BASE_URL=http://127.0.0.1:18083/v1 IOP_LONG_SMOKE_LONG_CHARS=384000 bash scripts/e2e-long-context-admission-smoke.sh --scenario all-long-slot-full --config build/dev-runtime/edge.yaml --out-dir /tmp/iop-long-admission-smoke'
```

Expected: all local checks pass; mock failure cases return non-zero; live preflight and three scenarios return 0; review stub includes actual stdout/stderr or exact saved output paths, final status snapshots, and Edge log evidence for long classification/queue reason.

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