<!-- task=m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke plan=1 tag=REVIEW_TEST -->

# Code Review Reference - REVIEW_TEST

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-06-23
task=m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke, plan=1, tag=REVIEW_TEST

## Roadmap Targets

- Milestone: `agent-roadmap/phase/update-plane-self-update-foundation/milestones/runtime-reconnect-config-refresh.md`
- Task ids:
  - `field-reconnect`: `toki-labs.com` dev-runtime에서 Edge restart 후 GX10 Linux Node와 OneXPlayer Windows Node가 bootstrap 재실행 없이 재접속한다.
  - `field-refresh`: `gx10-vllm=4`, `onexplayer-lemonade=3` capacity 변경을 refresh로 반영하고 Edge process restart 없이 `qwen3.6:35b` 동시 4개 요청이 성공한다.
  - `docs`: bootstrap/reconnect/refresh 운영 방법과 제한 사항이 `apps/edge/README.md`, `docs/edge-local-dev-guide.md`, agent-test field 기준에 반영된다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Prior active task: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke`
- Archived plan log: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/plan_cloud_G08_0.log`
- Archived review log: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_0.log`
- Verdict: FAIL
- Required summary:
  - S16 `field-reconnect` evidence missing. TEST-1 exited 1 before restart/reconnect evidence.
  - S17 `field-refresh` evidence missing. TEST-2 exited 1 before config check, refresh dry-run/apply, health/models, concurrent requests.
  - Plan verification command searched `iop-edge`, `./build/bin/iop-edge`, `./build/dev-runtime/iop-edge`; actual remote dev-runtime binary is `build/dev-runtime/bin/edge`.
  - `docs/edge-local-dev-guide.md` and agent-test field criteria need to align with the actual dev-runtime binary/config.
  - Final evidence check failed because `/tmp/iop-field-evidence/reconnect-after-status.json`, `reconnect-after-tcp.txt`, `refresh-apply.json`, and `refresh-concurrent-4.jsonl` were missing.
- Affected files:
  - `docs/edge-local-dev-guide.md`
  - `agent-test/local/edge-smoke.md`
  - `agent-test/local/rules.md` if actual field criteria changed
  - `CODE_REVIEW-cloud-G08.md`
- Verification evidence from prior loop:
  - Remote read-only recheck confirmed `build/dev-runtime/bin/edge=yes`, `command -v iop-edge` empty, `build/bin/iop-edge=no`, `build/dev-runtime/iop-edge=no`.
  - Remote ports `18001`, `18083`, `18084`, `19093` were closed.
  - Remote `build/dev-runtime/edge.yaml` had target capacities but no `refresh:` block.
  - Remote `build/dev-runtime` contains `bin/control-plane`, `bin/edge`, `bin/iop-node`, `control-plane.yaml`, `edge.yaml`, `node-codex.yaml`, `node-gx10-vllm.yaml`, and `node-onexplayer-lemonade.yaml`.
- Roadmap carryover: `field-reconnect`, `field-refresh`, and `docs` remain incomplete until this follow-up PASS writes `Roadmap Completion`.
- Narrow archive reread allowed if needed: only the two archived logs listed above.

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G08.md` -> `code_review_cloud_G08_N.log`, `PLAN-cloud-G08.md` -> `plan_cloud_G08_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_TEST-1] Dev-Runtime Verification Contract | [x] |
| [REVIEW_TEST-2] Field Reconnect Evidence | [ ] (blocked: 아래 `계획 대비 변경 사항` 참고) |
| [REVIEW_TEST-3] Field Refresh And Concurrent Requests | [ ] (blocked: 아래 `계획 대비 변경 사항` 참고) |
| [REVIEW_TEST-4] Docs And Field Rule Consistency | [x] (doc/contract 보정 완료, field 결과 대조는 baseline blocker로 미완) |

## 구현 체크리스트

- [x] `REVIEW_TEST-1` dev-runtime 검증 계약을 actual artifact에 맞춘다. 검증: local docs/agent-test command가 `build/dev-runtime/bin/edge` 또는 명시적 `EDGE_BIN` discovery를 사용하고, remote preflight가 binary/config/port 상태를 token 없이 기록한다.
- [ ] `REVIEW_TEST-2` 원격 dev-runtime baseline을 준비하고 Edge restart 후 bootstrap 재실행 없이 3개 Node가 회복되는 evidence를 수집한다. 검증: Control Plane status와 Edge node TCP connection 수가 3개 Node 기준으로 회복된다. (blocked: gx10-vllm Node가 macOS dev-runtime host에서 기동 불가, 2/3 Node만 연결)
- [ ] `REVIEW_TEST-3` `gx10-vllm=4`, `onexplayer-lemonade=3` capacity를 refresh apply로 반영하고 Edge PID 유지와 `qwen3.6:35b` 동시 4개 요청 성공 evidence를 수집한다. 검증: refresh apply JSON, Edge PID before/after 동일성, `/healthz`, `/v1/models`, 4-line concurrent JSONL이 존재한다. (blocked: remote dev-runtime edge 바이너리에 `config refresh` 명령/admin listener 부재)
- [x] `REVIEW_TEST-4` docs와 agent-test field 기준이 실제 field 결과와 일치하는지 확인하고 필요한 문서/agent-test 기준만 보정한다.
- [x] 최종 검증 명령을 모두 실행하고 실제 stdout/stderr를 `CODE_REVIEW-cloud-G08.md`에 붙여 넣는다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G08_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G08_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/`를 `agent-task/archive/YYYY/MM/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-runtime-reconnect-config-refresh/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G08.md`와 `CODE_REVIEW-cloud-G08.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

REVIEW_TEST-1과 REVIEW_TEST-4의 doc/agent-test 계약 보정은 계획대로 수행했다. REVIEW_TEST-2/REVIEW_TEST-3 field evidence 수집은 원격 dev-runtime baseline 자체가 이번 Milestone 코드를 담고 있지 않아 차단됐다. 이는 외부 환경/baseline blocker이므로 plan을 확장하지 않고 아래에 사실과 실제 출력으로 기록한다. (plan `범위 결정 근거` 및 stub 지침: 환경/서비스 blocker와 증거 공백은 사용자 리뷰가 아니라 검증 결과/후속 plan으로 처리)

차단 사실(모두 실제 원격 출력으로 확인, `검증 결과` 참고):

1. **remote dev-runtime이 이번 feature 이전 코드/바이너리다.** 원격 `/Users/toki/agent-work/iop-dev` HEAD는 `7398f32 update roadmap: archive inference-provider-extension milestone`이고 working tree가 divergent dirty 상태다. `apps/edge/internal/bootstrap/refresh_admin.go`가 원격에 존재하지 않는다. `build/dev-runtime/bin/edge` 빌드 시각은 `Jun 21 07:39 2026`로 config-refresh 작업(local `ff161b9`/`445716c`/`6706b5e`)보다 앞선다.
2. **remote edge 바이너리에 `config refresh` 명령 자체가 없다.** `edge config`의 하위 명령은 `check/init/print`뿐이고 `config refresh --mode ...`는 `Error: unknown flag: --mode`로 끝난다. refresh admin listener(`refresh.listen` 19093)도 바인딩되지 않는다(preflight에서 19093 CLOSED). → S17 `field-refresh`를 현재 baseline으로 재현 불가.
3. **`node-gx10-vllm`이 macOS dev-runtime host에서 기동 불가.** node-gx10-vllm 로그에 `bootstrap: workspace: mkdir /home/toki: operation not supported`. Linux workspace 경로(`/home/toki`)가 macOS host에서 실패해 edge 등록 직후 종료된다. Control Plane status에는 `mac-codex-node`, `onexplayer-lemonade-node` 2개만 connected. → S16의 "3개 Node 회복" 전제를 현재 baseline으로 충족 불가.
4. **dev-runtime edge id 불일치.** 실제 dev-runtime edge id는 `edge-toki-labs-dev`인데 plan/stub의 REVIEW_TEST-2/최종 검증 명령은 `edge-toki-labs/status`를 호출한다. 그 경로는 404를 반환하고, `set -e` + `grep` 조합 때문에 `reconnect-before-status.json`이 빈 파일로 남고 이후 evidence 단계가 실행되지 않았다. agent-test/local/edge-smoke.md에 `edge-toki-labs-dev` 기준을 추가해 다음 loop의 명령 계약을 바로잡았다.

후속 plan 제안(review 판단용): 다음 loop은 evidence 수집 전에 (a) 원격 dev-runtime을 이번 Milestone 코드로 동기화/재빌드하고, (b) gx10-vllm Node를 macOS host에서 기동 가능한 workspace로 두거나 실제 GX10 Linux host에서 연결하며, (c) 검증 명령의 edge id를 `edge-toki-labs-dev`로, refresh addr를 실제 `refresh.listen`으로 고정해야 한다. 원격 working tree가 divergent dirty 상태이므로 강제 sync/reset/rebuild는 이번 구현에서 수행하지 않았다(WIP 손실 위험, plan 범위 밖).

## 주요 설계 결정

- doc/agent-test 명령 계약은 `iop-edge` package 이름 대신 `EDGE_BIN=build/dev-runtime/bin/edge` 실제 artifact 경로를 사용하도록 보정했다(REVIEW_TEST-1). field guide는 dev-runtime 명령에 `EDGE_BIN`을 도입하고, edge-smoke.md dev-runtime baseline에 실제 바이너리 경로와 `edge-toki-labs-dev` edge id를 명시했다.
- `apps/edge/README.md`와 `docs/edge-local-dev-guide.md`의 refresh/reconnect 운영 문서(S18)는 선행 loop에서 이미 작성돼 working tree에 존재한다. 이번 loop은 본문을 다시 쓰지 않고 dev-runtime 명령 경로/edge id 정합성만 보정했다.
- 원격에 inert `refresh:` 블록을 `build/dev-runtime/edge.yaml`에 1회 삽입했다(plan 지정). 현재 바이너리가 refresh를 지원하지 않으므로 listener는 바인딩되지 않는다. sanitized snippet: `refresh:\n  enabled: true\n  listen: "127.0.0.1:19093"`. token/endpoint 원문은 기록하지 않았다.
- 원격에서 시작한 CP/Edge/Node(codex, onexplayer) 프로세스는 종료하지 않고 그대로 두었다. 다음 loop baseline 재구성 시 정리/재빌드가 필요하다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- dev-runtime field 명령이 actual remote artifact path와 일치하는지 확인한다.
- `field-reconnect` evidence가 bootstrap 재실행 없이 Edge restart 후 3 Node 회복을 보여주는지 확인한다.
- `field-refresh` evidence가 refresh apply, Edge PID 유지, four concurrent success를 모두 포함하는지 확인한다.
- docs와 agent-test field 기준이 field 결과와 모순되지 않는지 확인한다.
- token/API key/private endpoint 원문이 tracked docs, roadmap, review output에 노출되지 않았는지 확인한다.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.

### REVIEW_TEST-1 중간 검증
```bash
$ rg --sort path -n "\./iop-edge --config build/dev-runtime/edge.yaml|build/dev-runtime/iop-edge|build/bin/iop-edge" docs/edge-local-dev-guide.md agent-test/local/edge-smoke.md
$ echo "EXIT=$?"
EXIT=1
# no matches → pass (stale dev-runtime binary command 제거됨)
```

```bash
$ ssh toki@toki-labs.com 'set -euo pipefail
cd /Users/toki/agent-work/iop-dev
printf "edge_bin="; test -x build/dev-runtime/bin/edge && echo build/dev-runtime/bin/edge
printf "control_plane_bin="; test -x build/dev-runtime/bin/control-plane && echo build/dev-runtime/bin/control-plane
printf "node_bin="; test -x build/dev-runtime/bin/iop-node && echo build/dev-runtime/bin/iop-node
printf "refresh_block="; grep -n "^refresh:" build/dev-runtime/edge.yaml || echo absent
printf "capacity_lines=\n"; grep -nE "id: \"(gx10-vllm|onexplayer-lemonade)\"|capacity:" build/dev-runtime/edge.yaml | sed -E "s/(token: ).*/\1<masked>/"
for port in 18001 18083 18084 19093; do
  printf "port_%s=" "$port"
  (lsof -nP -iTCP:$port -sTCP:LISTEN 2>/dev/null | awk "NR>1{print \$1\":\"\$2; found=1} END{if(!found) print \"CLOSED\"}") || echo CLOSED
done
'
edge_bin=build/dev-runtime/bin/edge
control_plane_bin=build/dev-runtime/bin/control-plane
node_bin=build/dev-runtime/bin/iop-node
refresh_block=absent
capacity_lines=
106:          capacity: 4
111:      - id: "gx10-vllm"
118:        capacity: 4
136:          capacity: 3
141:      - id: "onexplayer-lemonade"
148:        capacity: 3
port_18001=CLOSED
port_18083=CLOSED
port_18084=CLOSED
port_19093=CLOSED
$ echo "EXIT=$?"
EXIT=0
# 바이너리 3종 존재, refresh 블록 absent, capacity gx10-vllm=4 / onexplayer-lemonade=3, 모든 port CLOSED.
# token/endpoint 원문 없음.
```

### REVIEW_TEST-2 중간 검증
```bash
$ ssh toki@toki-labs.com 'set -euo pipefail
cd /Users/toki/agent-work/iop-dev
mkdir -p /tmp/iop-field-evidence build/dev-runtime/logs
CP_BIN=build/dev-runtime/bin/control-plane
EDGE_BIN=build/dev-runtime/bin/edge
NODE_BIN=build/dev-runtime/bin/iop-node
CP_CONFIG=build/dev-runtime/control-plane.yaml
EDGE_CONFIG=build/dev-runtime/edge.yaml
test -x "$CP_BIN"
test -x "$EDGE_BIN"
test -x "$NODE_BIN"
python3 - <<'"'"'PY'"'"'
from pathlib import Path
p = Path("build/dev-runtime/edge.yaml")
s = p.read_text()
if "\nrefresh:" not in "\n" + s:
    marker = "\nopenai:"
    insert = "\nrefresh:\n  enabled: true\n  listen: \"127.0.0.1:19093\"\n"
    if marker not in s:
        raise SystemExit("openai marker not found in edge.yaml")
    p.write_text(s.replace(marker, insert + marker, 1))
PY
"$EDGE_BIN" --config "$EDGE_CONFIG" config check | tee /tmp/iop-field-evidence/dev-runtime-config-check.txt
if ! lsof -nP -iTCP:18001 -sTCP:LISTEN >/dev/null 2>&1; then
  nohup "$CP_BIN" --config "$CP_CONFIG" serve > build/dev-runtime/logs/control-plane.log 2>&1 &
  echo $! > build/dev-runtime/control-plane.pid
fi
if ! lsof -nP -iTCP:18083 -sTCP:LISTEN >/dev/null 2>&1; then
  nohup "$EDGE_BIN" --config "$EDGE_CONFIG" serve > build/dev-runtime/logs/edge.log 2>&1 &
  echo $! > build/dev-runtime/edge.pid
fi
for name in node-codex node-gx10-vllm node-onexplayer-lemonade; do
  cfg="build/dev-runtime/${name}.yaml"
  test -f "$cfg"
  if ! pgrep -f "iop-node.*${cfg}" >/dev/null 2>&1; then
    nohup "$NODE_BIN" --config "$cfg" serve > "build/dev-runtime/logs/${name}.log" 2>&1 &
    echo $! > "build/dev-runtime/${name}.pid"
  fi
done
for i in $(seq 1 120); do
  curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs/status > /tmp/iop-field-evidence/reconnect-before-status.json && break || true
  sleep 1
done
grep -E "mac-codex-node|gx10-vllm-node|onexplayer-lemonade-node" /tmp/iop-field-evidence/reconnect-before-status.json
(lsof -nP -iTCP:18084 -sTCP:ESTABLISHED 2>/dev/null || ss -tn state established "( sport = :18084 )" 2>/dev/null || true) | tee /tmp/iop-field-evidence/reconnect-before-tcp.txt
BEFORE_PID="$(pgrep -f "build/dev-runtime/bin/edge.*serve|edge.*build/dev-runtime/edge.yaml" | head -n 1)"
test -n "$BEFORE_PID"
kill "$BEFORE_PID"
for i in $(seq 1 20); do if ! kill -0 "$BEFORE_PID" 2>/dev/null; then break; fi; sleep 1; done
nohup "$EDGE_BIN" --config "$EDGE_CONFIG" serve > build/dev-runtime/logs/edge.log 2>&1 &
AFTER_PID="$!"
echo "$AFTER_PID" > build/dev-runtime/edge.pid
printf "before_pid=%s after_pid=%s bootstrap_rerun=none\n" "$BEFORE_PID" "$AFTER_PID" | tee /tmp/iop-field-evidence/reconnect-pids.txt
for i in $(seq 1 120); do
  curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs/status > /tmp/iop-field-evidence/reconnect-after-status.json && grep -E "mac-codex-node|gx10-vllm-node|onexplayer-lemonade-node" /tmp/iop-field-evidence/reconnect-after-status.json && break || true
  sleep 1
done
(lsof -nP -iTCP:18084 -sTCP:ESTABLISHED 2>/dev/null || ss -tn state established "( sport = :18084 )" 2>/dev/null || true) | tee /tmp/iop-field-evidence/reconnect-after-tcp.txt
'
OK build/dev-runtime/edge.yaml            # config check 통과, refresh 블록 삽입됨
curl: (22) The requested URL returned error: 404
curl: (22) The requested URL returned error: 404
... (위 404가 120회 반복) ...
$ echo "EXIT=$?"
EXIT=1
# BLOCKED. 원인:
# (1) 명령이 호출한 status 경로 /edges/edge-toki-labs/status 는 404 (실제 dev-runtime edge id = edge-toki-labs-dev).
#     `curl -fsS ... > reconnect-before-status.json` 가 매번 빈 파일을 남기고, 이어지는 grep 이 set -e 하에서 스크립트를 중단시켜
#     reconnect-before-tcp.txt / reconnect-pids.txt / reconnect-after-* 는 생성되지 않음.
# (2) 진단 결과 baseline 자체가 3 Node 회복을 만족 못 함. 후속 진단(아래) 참고.
$ # --- 후속 진단 (focused rerun) ---
$ ssh toki@toki-labs.com 'cd ... ; pgrep -fl "build/dev-runtime/bin/(edge|control-plane|iop-node)"'
41326 build/dev-runtime/bin/control-plane --config build/dev-runtime/control-plane.yaml serve
41330 build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml serve
41332 build/dev-runtime/bin/iop-node --config build/dev-runtime/node-codex.yaml serve
41336 build/dev-runtime/bin/iop-node --config build/dev-runtime/node-onexplayer-lemonade.yaml serve
# node-gx10-vllm 프로세스 없음.
$ ssh ... 'curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status | jq .nodes[].node_id'
"mac-codex-node"
"onexplayer-lemonade-node"
# 2/3 Node만 connected. 18084 established=4 (codex+onexplayer 양방향).
$ ssh ... 'tail build/dev-runtime/logs/node-gx10-vllm.log'
{"level":"info",...,"msg":"registered with edge","node_id":"gx10-vllm-node","alias":"gx10-vllm"}
{"level":"info",...,"msg":"disconnected from edge","transport_close_reason":"local_close",...}
[Fx] HOOK OnStart ... failed in 180ms: bootstrap: workspace: mkdir /home/toki: operation not supported
[Fx] ERROR  Failed to start: bootstrap: workspace: mkdir /home/toki: operation not supported
# gx10-vllm Node 는 Linux workspace(/home/toki)를 macOS dev-runtime host 에서 mkdir 하지 못해 종료.
# → S16 "Edge restart 후 3 Node 회복" 전제를 현재 baseline 으로 충족 불가.
```

### REVIEW_TEST-3 중간 검증
```bash
$ ssh toki@toki-labs.com 'set -euo pipefail
cd /Users/toki/agent-work/iop-dev
mkdir -p /tmp/iop-field-evidence
EDGE_BIN=build/dev-runtime/bin/edge
EDGE_CONFIG=build/dev-runtime/edge.yaml
REFRESH_ADDR="$(python3 - <<'"'"'PY'"'"'
from pathlib import Path
import re
s = Path("build/dev-runtime/edge.yaml").read_text()
m = re.search(r"(?ms)^refresh:\n(?:^[ \t]+.*\n)*?^[ \t]+listen:[ \t]*\"?([^\"\n]+)\"?", s)
print(m.group(1).strip() if m else "127.0.0.1:19093")
PY
)"
EDGE_PID_BEFORE="$(pgrep -f "build/dev-runtime/bin/edge.*serve|edge.*build/dev-runtime/edge.yaml" | head -n 1)"
test -n "$EDGE_PID_BEFORE"
"$EDGE_BIN" --config "$EDGE_CONFIG" config refresh --mode dry-run --addr "$REFRESH_ADDR" | tee /tmp/iop-field-evidence/refresh-dry-run.json
"$EDGE_BIN" --config "$EDGE_CONFIG" config refresh --mode apply --addr "$REFRESH_ADDR" | tee /tmp/iop-field-evidence/refresh-apply.json
EDGE_PID_AFTER="$(pgrep -f "build/dev-runtime/bin/edge.*serve|edge.*build/dev-runtime/edge.yaml" | head -n 1)"
printf "edge_pid_before=%s edge_pid_after=%s\n" "$EDGE_PID_BEFORE" "$EDGE_PID_AFTER" | tee /tmp/iop-field-evidence/refresh-pid.txt
test "$EDGE_PID_BEFORE" = "$EDGE_PID_AFTER"
curl -fsS http://toki-labs.com:18083/healthz | tee /tmp/iop-field-evidence/refresh-healthz.txt
curl -fsS http://toki-labs.com:18083/v1/models | tee /tmp/iop-field-evidence/refresh-models.json
python3 - <<'"'"'PY'"'"' | tee /tmp/iop-field-evidence/refresh-concurrent-4.jsonl
import concurrent.futures, json, urllib.request
url = "http://toki-labs.com:18083/v1/chat/completions"
payload = {
  "model": "qwen3.6:35b",
  "messages": [{"role": "user", "content": "Reply with exactly IOP_FIELD_REFRESH_OK."}],
  "max_tokens": 32,
  "temperature": 0
}
def one(i):
    data = json.dumps(payload).encode()
    req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=300) as resp:
        body = json.loads(resp.read().decode())
    return {"index": i, "status": "ok", "id": body.get("id"), "model": body.get("model"), "usage": body.get("usage"), "content": body.get("choices", [{}])[0].get("message", {}).get("content", "")}
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as ex:
    for item in ex.map(one, range(4)):
        print(json.dumps(item, ensure_ascii=False))
PY
test "$(wc -l < /tmp/iop-field-evidence/refresh-concurrent-4.jsonl)" = "4"
'
(미실행) BLOCKED — REVIEW_TEST-3는 fixed contract 그대로 실행하면 baseline 미충족으로 의미 있는 evidence를 만들 수 없어
focused 진단으로 차단 원인을 확정했다.
$ # --- focused 진단: remote edge 바이너리의 refresh 지원 여부 ---
$ ssh ... 'build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr 127.0.0.1:19093'
Error: unknown flag: --mode
Usage:
  edge config [command]
Available Commands:
  check       Validate edge config can be loaded
  init        Write an edge.yaml template
  print       Print effective edge config as YAML
# 원격 edge 바이너리에 `config refresh` 하위 명령 자체가 없음 (check/init/print 만 존재).
$ ssh ... 'ls apps/edge/internal/bootstrap/refresh_admin.go'
ls: apps/edge/internal/bootstrap/refresh_admin.go: No such file or directory   # 원격 source 에 refresh admin listener 부재
$ ssh ... 'git log --oneline -1; stat -f "%Sm %N" build/dev-runtime/bin/edge'
7398f32 update roadmap: archive inference-provider-extension milestone and update phase docs
Jun 21 07:39:34 2026 build/dev-runtime/bin/edge   # config-refresh 작업 이전 빌드
# → S17 field-refresh(refresh apply, PID 유지, 동시 4 요청)는 현재 remote baseline 으로 재현 불가.
```

### REVIEW_TEST-4 중간 검증
```bash
$ git diff -- apps/edge/README.md docs/edge-local-dev-guide.md
# apps/edge/README.md: refresh/reconnect 운영 섹션(선행 loop 작성)이 working tree 에 존재.
#   +iop-edge config refresh --mode dry-run/apply ... + "## Config Refresh와 Node Reconnect" 섹션.
# docs/edge-local-dev-guide.md (이번 loop 보정분):
#   @@ section 2: +refresh:\n  enabled: true\n  listen: "127.0.0.1:19093"
#   @@ section 6: dev-runtime 명령을 `EDGE_BIN=build/dev-runtime/bin/edge` 로 교체
#   -./iop-edge --config build/dev-runtime/edge.yaml config check
#   +EDGE_BIN=build/dev-runtime/bin/edge
#   +"$EDGE_BIN" --config build/dev-runtime/edge.yaml config check
#   +"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr 127.0.0.1:19093
#   +"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode apply --addr 127.0.0.1:19093
```

```bash
$ rg --sort path -n "build/dev-runtime/bin/edge|EDGE_BIN|config refresh|Node reconnect|gx10-vllm|onexplayer-lemonade" docs/edge-local-dev-guide.md agent-test/local/edge-smoke.md agent-test/local/rules.md
docs/edge-local-dev-guide.md:116:- provider candidate: `gx10-vllm` on `gx10-vllm-node`, capacity `4`
docs/edge-local-dev-guide.md:117:- provider candidate: `onexplayer-lemonade` on `onexplayer-lemonade-node`, capacity `3`
docs/edge-local-dev-guide.md:120:capacity, provider mapping, model alias 변경은 ... `config refresh`로 반영한다. ...
docs/edge-local-dev-guide.md:122:dev-runtime 번들은 바이너리를 `build/dev-runtime/bin/edge`에 둔다. 명령은 그 경로를 `EDGE_BIN`으로 잡고 실행한다.
docs/edge-local-dev-guide.md:125:EDGE_BIN=build/dev-runtime/bin/edge
docs/edge-local-dev-guide.md:126:"$EDGE_BIN" --config build/dev-runtime/edge.yaml config check
docs/edge-local-dev-guide.md:127:"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr 127.0.0.1:19093
docs/edge-local-dev-guide.md:128:"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode apply --addr 127.0.0.1:19093
docs/edge-local-dev-guide.md:131:Node host OS 재부팅은 필요하지 않다. ... Node reconnect ... 기본 `10s` 간격으로 최대 `10`회 ...
docs/edge-local-dev-guide.md:139:5. capacity를 `gx10-vllm=4`, `onexplayer-lemonade=3`로 ... refresh apply ...
docs/edge-local-dev-guide.md:141:7. ... aggregate 배정 기대값은 `gx10-vllm` 2개, `onexplayer-lemonade` 2개다.
agent-test/local/edge-smoke.md:116:- dev-runtime 바이너리: `build/dev-runtime/bin/edge`, ... `EDGE_BIN=build/dev-runtime/bin/edge` ...
agent-test/local/edge-smoke.md:124:- provider candidates: `gx10-vllm` ... `onexplayer-lemonade` ...
agent-test/local/edge-smoke.md:126:- capacity baseline: `gx10-vllm=4`, `onexplayer-lemonade=3`
agent-test/local/edge-smoke.md:141:- ... `config refresh --mode apply`로 반영한다. ...
agent-test/local/edge-smoke.md:151:5. ... `config refresh --mode dry-run`과 `--mode apply`를 순서대로 실행한다.
agent-test/local/edge-smoke.md:154:8. `gx10-vllm=4`, `onexplayer-lemonade=3` 기준이면 ... `gx10-vllm` 2개, `onexplayer-lemonade` 2개 ...
agent-test/local/rules.md:52:- Edge config 변경 중 provider capacity ... `config refresh --mode apply`로 반영한다. ...
agent-test/local/rules.md:53:- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. ... `10s` 간격 ... `10`회 ...
# positive: 새 actual dev-runtime baseline(바이너리 경로/edge id/capacity/refresh) 노출됨.
```

```bash
$ ! rg --sort path -n "hot reload되지 않는다|자동 재등록 루프를 보장하지 않으므로|Edge process restart 후 새 capacity|\./iop-edge --config build/dev-runtime/edge.yaml|build/dev-runtime/iop-edge|build/bin/iop-edge" apps/edge/README.md docs/edge-local-dev-guide.md agent-test/local/rules.md agent-test/local/edge-smoke.md
$ echo "EXIT=$?"
EXIT=0
# negative: stale wording / stale binary command 매치 없음 → pass.
```

### 최종 검증
```bash
$ git diff --check
$ echo "EXIT=$?"
EXIT=0
# whitespace/conflict 오류 없음.
```

```bash
$ ! rg --sort path -n "hot reload되지 않는다|자동 재등록 루프를 보장하지 않으므로|Edge process restart 후 새 capacity|\./iop-edge --config build/dev-runtime/edge.yaml|build/dev-runtime/iop-edge|build/bin/iop-edge" apps/edge/README.md docs/edge-local-dev-guide.md agent-test/local/rules.md agent-test/local/edge-smoke.md
$ echo "EXIT=$?"
EXIT=0
# no matches → pass.
```

```bash
$ ssh toki@toki-labs.com 'test -s /tmp/iop-field-evidence/reconnect-after-status.json && test -s /tmp/iop-field-evidence/reconnect-after-tcp.txt && test -s /tmp/iop-field-evidence/reconnect-pids.txt && test -s /tmp/iop-field-evidence/refresh-apply.json && test -s /tmp/iop-field-evidence/refresh-pid.txt && test -s /tmp/iop-field-evidence/refresh-concurrent-4.jsonl && test "$(wc -l < /tmp/iop-field-evidence/refresh-concurrent-4.jsonl)" = "4"'
$ echo "EXIT=$?"
EXIT=1
# FAIL (예상된 차단). field evidence inventory:
#   dev-runtime-config-check.txt = NONEMPTY (31b, "OK build/dev-runtime/edge.yaml")
#   reconnect-before-status.json = EMPTY        (edge id 404 → 빈 파일)
#   reconnect-before-tcp.txt     = MISSING
#   reconnect-pids.txt           = MISSING
#   reconnect-after-status.json  = MISSING
#   reconnect-after-tcp.txt      = MISSING
#   refresh-apply.json           = MISSING
#   refresh-pid.txt              = MISSING
#   refresh-concurrent-4.jsonl   = MISSING
# 차단 원인은 `계획 대비 변경 사항` 1~4 참고 (remote baseline 이 이번 Milestone 코드 미반영 + gx10 Node macOS 기동 불가 + edge id 불일치).
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
| Roadmap Targets | Fixed at stub creation from plan | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan | Previous loop context; only listed archive paths may be reread narrowly |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` to `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` to `[x]` only; final checkbox is mandatory before saving |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 | Implementing agent | Fill actual stdout/stderr only |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - implementation deviation: Pass
  - verification trust: Fail
  - spec conformance: Fail
- 발견된 문제:
  - Required: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_1.log:73` `REVIEW_TEST-2`와 `REVIEW_TEST-3`가 미완료이고, 최종 evidence inventory도 `reconnect-after-*`, `refresh-apply.json`, `refresh-pid.txt`, `refresh-concurrent-4.jsonl`을 모두 missing/empty로 기록한다. SDD S16/S17의 필수 field evidence가 없으므로 PASS할 수 없다. 원격 baseline을 복구한 뒤 Edge restart 후 3 Node reconnect, refresh apply, Edge PID 유지, `/healthz`, `/v1/models`, 4-line concurrent JSONL을 실제 산출해야 한다.
  - Required: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_1.log:110` 원격 dev-runtime이 이번 refresh 구현을 담고 있지 않고 `edge config` 하위 명령도 `check/init/print`뿐이라 `config refresh --mode ...`가 실행될 수 없다. field 검증 전에 remote checkout/artifact를 이번 Milestone 코드로 안전하게 동기화/재빌드하고, `apps/edge/internal/bootstrap/refresh_admin.go` 존재와 `edge config refresh` 명령 노출을 확인해야 한다.
- 다음 단계:
  - WARN/FAIL follow-up: 같은 active task directory에 다음 `PLAN-cloud-G08.md`와 `CODE_REVIEW-cloud-G08.md`를 작성한다. 사용자 리뷰 게이트는 트리거하지 않는다.
