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

# Plan - REVIEW_REVIEW_REVIEW_TEST

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

`CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 반드시 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 review ready로 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션을 채우고 멈춘다. 외부 환경/secret/service blocker, 일반 범위 조정, 증거 공백은 사용자 리뷰가 아니라 검증 결과나 후속 plan으로 기록한다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다.

## 배경

이전 루프는 clean review workdir 재빌드와 `edge config refresh --mode` 노출 증거는 확보했지만, GX10 SSH publickey 차단으로 SDD S16/S17 field evidence를 만들지 못했다. 또한 최종 원격 검증이 local-only `preflight-local.txt`를 원격 `/tmp`에서 검사해 실패 위치가 흐려졌다. 이번 follow-up은 GX10 접근 preflight를 먼저 통과시킨 뒤에만 dev-runtime을 재기동하고, local/remote evidence 검증을 분리한다.

## 사용자 리뷰 요청 흐름

선택된 Milestone lock 결정만 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. GX10 SSH 접근 차단, secret/service 준비, field evidence 공백은 user-review gate가 아니라 검증 blocker 또는 follow-up evidence다. 구현 중 직접 사용자에게 묻지 말고, code-review가 요청을 검증한 뒤 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Archive Evidence Snapshot

- Prior active task: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke`
- Current archived plan/review:
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/plan_cloud_G08_3.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_3.log`
- Earlier archived logs:
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/plan_cloud_G08_0.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/plan_cloud_G08_1.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/plan_cloud_G08_2.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_0.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_1.log`
  - `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_2.log`
- Verdicts: FAIL, FAIL, superseded, FAIL.
- Required summary:
  - S16/S17 evidence remains missing. `code_review_cloud_G08_3.log` records `REVIEW_REVIEW_TEST-3` as unchecked and says reconnect/refresh evidence files were not created.
  - Read-only reviewer recheck found remote `/tmp/iop-field-evidence/review-config-refresh-help.txt` with `--mode`, but `reconnect-before-status.json` was size 0 and reconnect/refresh files were missing.
  - Final verification must not check local `/tmp/iop-field-evidence/preflight-local.txt` on the remote host.
- Affected files/areas:
  - `CODE_REVIEW-cloud-G08.md`
  - Remote clean review workdir `/Users/toki/agent-work/iop-field-review`
  - Remote evidence directory `/tmp/iop-field-evidence`
  - Local evidence file `/tmp/iop-field-evidence/preflight-local.txt`
- Roadmap carryover: `field-reconnect`, `field-refresh`, and `docs` remain incomplete until PASS writes `Roadmap Completion`.
- Narrow archive reread allowed if needed: only the eight archived logs listed above.

## 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

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-roadmap/current.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-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-contract/index.md`
- `agent-contract/provided/openai-compatible-api.md`
- `agent-roadmap/phase/update-plane-self-update-foundation/milestones/runtime-reconnect-config-refresh.md`
- `agent-roadmap/sdd/update-plane-self-update-foundation/runtime-reconnect-config-refresh/SDD.md`
- `apps/edge/README.md`
- `docs/edge-local-dev-guide.md`
- `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/PLAN-cloud-G08.md`
- `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/CODE_REVIEW-cloud-G08.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/update-plane-self-update-foundation/runtime-reconnect-config-refresh/SDD.md`
- 상태: `[승인됨]`, SDD 잠금 해제, 사용자 리뷰 없음.
- Target scenarios:
  - S16 -> `field-reconnect`: dev-runtime에 mac-codex, GX10, OneXPlayer Node가 연결된 상태에서 Edge restart 후 bootstrap 재실행 없이 3개 Node 연결 회복.
  - S17 -> `field-refresh`: `gx10-vllm=4`, `onexplayer-lemonade=3` refresh apply 후 Edge restart 없이 `qwen3.6:35b` 동시 4개 요청 성공.
  - S18 -> `docs`: docs diff와 agent-test field rule diff.
- Evidence Map rows:
  - S16: remote dev field status와 TCP connection evidence.
  - S17: remote dev 4 concurrent OpenAI-compatible requests와 refresh result evidence.
  - S18: docs diff와 agent-test field rule diff.
- 이번 checklist는 S16/S17 evidence를 먼저 복구하고, S18 문서 기준이 최종 field evidence와 모순되지 않는지 다시 확인하도록 구성한다.

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md` present/read.
- Matched profile: `agent-test/local/edge-smoke.md`.
- 적용 기준: remote runner `ssh toki@toki-labs.com`, clean review root `/Users/toki/agent-work/iop-field-review`, dev-runtime Edge id `edge-toki-labs-dev`, Control Plane status `http://127.0.0.1:18001`, Edge OpenAI-compatible `http://toki-labs.com:18083/v1`, Edge-Node TCP `toki-labs.com:18084`, GX10 `192.168.0.91`, model alias `qwen3.6:35b`, capacity `gx10-vllm=4`, `onexplayer-lemonade=3`.
- 테스트 환경 프리플라이트:
  - Local runtime source paths must be clean before `git archive HEAD`.
  - Remote `/Users/toki/agent-work/iop-dev` can remain dirty/stale and must not be reset, cleaned, pulled, or overwritten.
  - GX10 SSH access must be proven before any dev-runtime kill/restart.
  - If GX10 SSH still returns `Permission denied`, stop before remote mutation and record blocker evidence in `CODE_REVIEW-cloud-G08.md`; do not create USER_REVIEW.
- Secret/API key/private endpoint 원문은 tracked docs, roadmap, review output에 기록하지 않는다.

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

- `field-reconnect`: S16 remote field status/TCP/reconnect PID evidence가 없다.
- `field-refresh`: S17 refresh dry-run/apply, Edge PID 유지, `/healthz`, `/v1/models`, 4-line concurrent JSONL evidence가 없다.
- `docs`: docs diff는 있지만 S16/S17 field evidence와의 최종 cross-check가 남았다.

### 심볼 참조

- none. 이 follow-up은 Go symbol rename/remove를 하지 않는다.

### 분할 판단

- 기존 split subtask `07+01,03,04,05,06_field_docs_smoke`를 유지한다.
- Direct predecessors `01`, `03`, `04`, `05`, `06`는 이전 루프에서 충족된 것으로 이어받는다.
- 이번 follow-up은 별도 subtask로 쪼개지 않는다. GX10 access preflight, clean rebuild, field reconnect/refresh evidence, final verification split은 같은 S16/S17 증거 체인의 순차 단계다.

### 범위 결정 근거

- Go source 기능 수정은 하지 않는다. 현 결함은 field evidence와 verification contract 공백이다.
- `/Users/toki/agent-work/iop-dev`는 source나 binary로 재사용하지 않고 ignored runtime YAML source로만 사용한다.
- GX10 SSH가 막혀 있으면 운영 중 dev-runtime을 kill/restart하지 않는다.
- docs 추가 수정은 field evidence와 모순이 발견될 때만 수행한다.

### 빌드 등급

- `cloud-G08`: 원격 runner, GX10 host access, process lifecycle, field OpenAI-compatible requests, evidence trust diagnosis가 중심이라 local bounded 조건을 만족하지 않는다.

## 구현 체크리스트

- [ ] `REVIEW_REVIEW_REVIEW_TEST-1` GX10 접근 preflight를 통과한 뒤 clean dev-runtime으로 S16/S17 field evidence를 수집한다. 검증: 3개 Node reconnect status/TCP, `bootstrap_rerun=none`, refresh dry-run/apply, Edge PID 유지, `/healthz`, `/v1/models`, 4-line concurrent JSONL이 존재한다.
- [ ] `REVIEW_REVIEW_REVIEW_TEST-2` 최종 검증을 local artifact와 remote artifact 검사로 분리하고 실제 stdout/stderr를 기록한다. 검증: local `preflight-local.txt`는 local에서, remote field evidence는 remote에서 검사하며 `FINAL_VERIFICATION=PASS`가 출력된다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

`REVIEW_REVIEW_REVIEW_TEST-1`에서 GX10 SSH preflight가 성공해야 dev-runtime 재기동과 field evidence 수집을 진행한다. GX10 접근이 막혀 있으면 원격 프로세스를 건드리지 말고 blocker output만 기록한다. `REVIEW_REVIEW_REVIEW_TEST-2`는 evidence 수집 후 실행한다.

### [REVIEW_REVIEW_REVIEW_TEST-1] GX10 Access And Field Evidence

문제: `code_review_cloud_G08_3.log:76`과 `code_review_cloud_G08_3.log:82`는 field evidence 항목이 미완료라고 기록한다. SDD S16/S17은 remote dev field status/TCP evidence와 4개 동시 OpenAI-compatible request evidence를 필수로 요구한다.

해결 방법: local source cleanliness와 GX10 SSH access를 먼저 증명한다. GX10 접근이 성공한 경우에만 clean review workdir를 재빌드하고, CP/Edge/mac Node/OneXPlayer Node/GX10 Node를 review-root 기준으로 기동해 reconnect와 refresh evidence를 수집한다.

수정 파일 및 체크리스트:

- [ ] local `/tmp/iop-field-evidence/preflight-local.txt`를 생성하고 runtime source dirty 여부를 기록한다.
- [ ] remote `/tmp/iop-field-evidence/preflight-remote.txt`와 `preflight-gx10.txt`를 생성한다.
- [ ] GX10 SSH가 실패하면 remote mutation 없이 중단하고 실패 stdout/stderr를 review stub에 기록한다.
- [ ] GX10 SSH가 성공하면 `/Users/toki/agent-work/iop-field-review`를 current `HEAD`로 재생성하고 dev-runtime binaries를 빌드한다.
- [ ] S16 reconnect evidence와 S17 refresh evidence를 `/tmp/iop-field-evidence`에 저장한다.
- [ ] token/API key/private endpoint 원문을 출력하지 않는다.

테스트 작성: 새 automated test는 작성하지 않는다. 이 항목은 SDD S16/S17 field evidence 수집이다.

중간 검증:

```bash
mkdir -p /tmp/iop-field-evidence
{
  echo "local_head=$(git rev-parse --short HEAD)"
  git status --branch --short
  if test -n "$(git status --porcelain -- apps/edge/cmd apps/edge/internal apps/node/cmd apps/node/internal apps/control-plane/cmd apps/control-plane/internal packages/go proto configs Makefile go.mod go.sum)"; then
    echo "runtime_source_dirty=yes"
    git status --porcelain -- apps/edge/cmd apps/edge/internal apps/node/cmd apps/node/internal apps/control-plane/cmd apps/control-plane/internal packages/go proto configs Makefile go.mod go.sum
    exit 2
  fi
  echo "runtime_source_dirty=no"
} 2>&1 | tee /tmp/iop-field-evidence/preflight-local.txt

ssh toki@toki-labs.com 'set -euo pipefail
mkdir -p /tmp/iop-field-evidence
{
  echo "runner_uname=$(uname -s) $(uname -m)"
  cd /Users/toki/agent-work/iop-dev || exit 0
  echo "remote_head=$(git rev-parse --short HEAD 2>/dev/null || true)"
  echo "remote_branch=$(git branch --show-current 2>/dev/null || true)"
  echo "remote_dirty_count=$(git status --porcelain 2>/dev/null | wc -l | tr -d " ")"
  test -x build/dev-runtime/bin/edge && echo "edge_bin=yes" || echo "edge_bin=no"
  build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help 2>&1 | sed -n "1,80p" || true
  for port in 18001 18083 18084 19093; do
    printf "port_%s=" "$port"
    lsof -nP -iTCP:"$port" -sTCP:LISTEN -sTCP:ESTABLISHED 2>/dev/null | awk "NR>1 {print \$1\":\"\$2}" | paste -sd "," - || echo CLOSED
  done
} 2>&1 | tee /tmp/iop-field-evidence/preflight-remote.txt
GX10_HOST=192.168.0.91
if ! ssh -o BatchMode=yes -o ConnectTimeout=10 toki@"$GX10_HOST" "printf \"gx10_uname=\"; uname -s; printf \"gx10_arch=\"; uname -m; printf \"gx10_go=\"; command -v go || true" 2>&1 | tee /tmp/iop-field-evidence/preflight-gx10.txt; then
  echo "gx10_ssh=blocked; stopping before dev-runtime mutation" | tee -a /tmp/iop-field-evidence/preflight-gx10.txt
  exit 10
fi'
```

Expected: exit 0 before continuing. Exit 10 means external GX10 access is still blocked; fill the review stub with the output and stop without mutating dev-runtime.

Field evidence command after preflight success:

```bash
test -z "$(git status --porcelain -- apps/edge/cmd apps/edge/internal apps/node/cmd apps/node/internal apps/control-plane/cmd apps/control-plane/internal packages/go proto configs Makefile go.mod go.sum)"
git archive --format=tar HEAD | ssh toki@toki-labs.com 'set -euo pipefail
REVIEW_ROOT=/Users/toki/agent-work/iop-field-review
SOURCE_RUNTIME=/Users/toki/agent-work/iop-dev/build/dev-runtime
rm -rf "$REVIEW_ROOT"
mkdir -p "$REVIEW_ROOT"
tar -xf - -C "$REVIEW_ROOT"
cd "$REVIEW_ROOT"
mkdir -p build/dev-runtime/bin build/dev-runtime/logs /tmp/iop-field-evidence
cp "$SOURCE_RUNTIME"/*.yaml build/dev-runtime/
/opt/homebrew/bin/go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge
/opt/homebrew/bin/go build -trimpath -o build/dev-runtime/bin/control-plane ./apps/control-plane/cmd/control-plane
/opt/homebrew/bin/go build -trimpath -o build/dev-runtime/bin/iop-node ./apps/node/cmd/node
GOOS=linux GOARCH=arm64 /opt/homebrew/bin/go build -trimpath -o build/dev-runtime/bin/iop-node-linux-arm64 ./apps/node/cmd/node
build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config check | tee /tmp/iop-field-evidence/review-config-check.txt
build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help | tee /tmp/iop-field-evidence/review-config-refresh-help.txt
grep -q -- "--mode" /tmp/iop-field-evidence/review-config-refresh-help.txt
GX10_HOST=192.168.0.91
ssh -o BatchMode=yes toki@"$GX10_HOST" "true"
{
  for pat in "/Users/toki/agent-work/iop-dev/build/dev-runtime/(bin/)?control-plane.*control-plane.yaml" "/Users/toki/agent-work/iop-dev/build/dev-runtime/(bin/)?edge.*edge.yaml" "/Users/toki/agent-work/iop-dev/build/dev-runtime/(bin/)?iop-node.*node-" "/Users/toki/agent-work/iop-field-review/build/dev-runtime/(bin/)?control-plane.*control-plane.yaml" "/Users/toki/agent-work/iop-field-review/build/dev-runtime/(bin/)?edge.*edge.yaml" "/Users/toki/agent-work/iop-field-review/build/dev-runtime/(bin/)?iop-node.*node-"; do
    pgrep -af "$pat" || true
  done
} | tee /tmp/iop-field-evidence/pre-kill-processes.txt
awk "{print \$1}" /tmp/iop-field-evidence/pre-kill-processes.txt | xargs -r kill
sleep 2
nohup "$REVIEW_ROOT/build/dev-runtime/bin/control-plane" --config "$REVIEW_ROOT/build/dev-runtime/control-plane.yaml" serve > "$REVIEW_ROOT/build/dev-runtime/logs/control-plane.log" 2>&1 &
echo $! > "$REVIEW_ROOT/build/dev-runtime/control-plane.pid"
nohup "$REVIEW_ROOT/build/dev-runtime/bin/edge" --config "$REVIEW_ROOT/build/dev-runtime/edge.yaml" serve > "$REVIEW_ROOT/build/dev-runtime/logs/edge.log" 2>&1 &
echo $! > "$REVIEW_ROOT/build/dev-runtime/edge.pid"
for name in node-codex node-onexplayer-lemonade; do
  nohup "$REVIEW_ROOT/build/dev-runtime/bin/iop-node" --config "$REVIEW_ROOT/build/dev-runtime/${name}.yaml" serve > "$REVIEW_ROOT/build/dev-runtime/logs/${name}.log" 2>&1 &
  echo $! > "$REVIEW_ROOT/build/dev-runtime/${name}.pid"
done
ssh toki@"$GX10_HOST" "mkdir -p ~/iop-field-review/bin ~/iop-field-review/logs"
scp "$REVIEW_ROOT/build/dev-runtime/bin/iop-node-linux-arm64" toki@"$GX10_HOST":~/iop-field-review/bin/iop-node
scp "$REVIEW_ROOT/build/dev-runtime/node-gx10-vllm.yaml" toki@"$GX10_HOST":~/iop-field-review/node-gx10-vllm.yaml
ssh toki@"$GX10_HOST" "pkill -f \"iop-field-review/bin/iop-node.*node-gx10-vllm.yaml\" || true; chmod +x ~/iop-field-review/bin/iop-node; nohup ~/iop-field-review/bin/iop-node --config ~/iop-field-review/node-gx10-vllm.yaml serve > ~/iop-field-review/logs/node-gx10-vllm.log 2>&1 & echo \\\$! > ~/iop-field-review/node-gx10-vllm.pid"
EDGE_ID=edge-toki-labs-dev
for i in $(seq 1 120); do
  curl -fsS "http://127.0.0.1:18001/edges/${EDGE_ID}/status" > /tmp/iop-field-evidence/reconnect-before-status.json && grep -q "mac-codex-node" /tmp/iop-field-evidence/reconnect-before-status.json && grep -q "gx10-vllm-node" /tmp/iop-field-evidence/reconnect-before-status.json && grep -q "onexplayer-lemonade-node" /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 | tee /tmp/iop-field-evidence/reconnect-before-tcp.txt
BEFORE_PID="$(cat build/dev-runtime/edge.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 "$REVIEW_ROOT/build/dev-runtime/bin/edge" --config "$REVIEW_ROOT/build/dev-runtime/edge.yaml" serve > "$REVIEW_ROOT/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_ID}/status" > /tmp/iop-field-evidence/reconnect-after-status.json && grep -q "mac-codex-node" /tmp/iop-field-evidence/reconnect-after-status.json && grep -q "gx10-vllm-node" /tmp/iop-field-evidence/reconnect-after-status.json && grep -q "onexplayer-lemonade-node" /tmp/iop-field-evidence/reconnect-after-status.json && break || true
  sleep 1
done
grep -E "mac-codex-node|gx10-vllm-node|onexplayer-lemonade-node" /tmp/iop-field-evidence/reconnect-after-status.json
lsof -nP -iTCP:18084 -sTCP:ESTABLISHED 2>/dev/null | tee /tmp/iop-field-evidence/reconnect-after-tcp.txt
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="$(cat build/dev-runtime/edge.pid)"
build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr "$REFRESH_ADDR" | tee /tmp/iop-field-evidence/refresh-dry-run.json
build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --mode apply --addr "$REFRESH_ADDR" | tee /tmp/iop-field-evidence/refresh-apply.json
EDGE_PID_AFTER="$(cat build/dev-runtime/edge.pid)"
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": "Return the word ok."}], "max_tokens": 4, "stream": False}
body = json.dumps(payload).encode()
def one(i):
    req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.loads(resp.read())
    return {"index": i, "ok": True, "model": data.get("model"), "choices": len(data.get("choices", []))}
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as ex:
    for row in ex.map(one, range(4)):
        print(json.dumps(row, sort_keys=True))
PY'
```

Expected: command exits 0, status JSON before/after contains all three node ids, TCP evidence is nonempty, `reconnect-pids.txt` contains `bootstrap_rerun=none`, Edge PID before/after refresh is equal, and `refresh-concurrent-4.jsonl` has 4 JSON lines with `"ok": true`.

### [REVIEW_REVIEW_REVIEW_TEST-2] Split Final Verification

문제: `code_review_cloud_G08_3.log:419` checks `preflight-local.txt` on the remote host even though it is created locally. Reviewer read-only recheck found remote `preflight-local.txt` missing, so the final verification command can fail before it checks S16/S17 evidence.

해결 방법: local artifact checks run locally, remote field evidence checks run remotely. The final remote check must include only remote files.

수정 파일 및 체크리스트:

- [ ] final verification output includes a local block checking `/tmp/iop-field-evidence/preflight-local.txt`.
- [ ] final verification output includes a remote block checking remote evidence files only.
- [ ] final verification output includes `FINAL_VERIFICATION=PASS` only after both blocks pass.

테스트 작성: 새 automated test는 작성하지 않는다. 이 항목은 verification contract correction이다.

중간 검증:

```bash
test -s /tmp/iop-field-evidence/preflight-local.txt
grep -q "runtime_source_dirty=no" /tmp/iop-field-evidence/preflight-local.txt
ssh toki@toki-labs.com 'test ! -e /tmp/iop-field-evidence/preflight-local.txt || true'
```

Expected: local preflight file exists locally. The remote command does not require or inspect remote `preflight-local.txt`.

## 수정 파일 요약

| 파일/영역 | 항목 |
|----------|------|
| `CODE_REVIEW-cloud-G08.md` | REVIEW_REVIEW_REVIEW_TEST-1, REVIEW_REVIEW_REVIEW_TEST-2 |
| Remote `/tmp/iop-field-evidence/**` | REVIEW_REVIEW_REVIEW_TEST-1, REVIEW_REVIEW_REVIEW_TEST-2 |
| Remote `/Users/toki/agent-work/iop-field-review/**` | REVIEW_REVIEW_REVIEW_TEST-1 |

## 최종 검증

```bash
git diff --check
test -s /tmp/iop-field-evidence/preflight-local.txt
grep -q "runtime_source_dirty=no" /tmp/iop-field-evidence/preflight-local.txt
rg --sort path -n "edge-toki-labs($|[^-])|edge-toki-labs[^-]dev|/Users/toki/agent-work/iop-dev.*config refresh" apps/edge/README.md docs/edge-local-dev-guide.md agent-test/local/edge-smoke.md agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/PLAN-cloud-G08.md agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/CODE_REVIEW-cloud-G08.md || true
ssh toki@toki-labs.com 'set -euo pipefail
for f in preflight-remote.txt preflight-gx10.txt review-config-check.txt review-config-refresh-help.txt reconnect-before-status.json reconnect-before-tcp.txt reconnect-pids.txt reconnect-after-status.json reconnect-after-tcp.txt refresh-dry-run.json refresh-apply.json refresh-pid.txt refresh-healthz.txt refresh-models.json refresh-concurrent-4.jsonl; do
  test -s "/tmp/iop-field-evidence/$f"
done
grep -q -- "--mode" /tmp/iop-field-evidence/review-config-refresh-help.txt
grep -q "mac-codex-node" /tmp/iop-field-evidence/reconnect-after-status.json
grep -q "gx10-vllm-node" /tmp/iop-field-evidence/reconnect-after-status.json
grep -q "onexplayer-lemonade-node" /tmp/iop-field-evidence/reconnect-after-status.json
grep -q "bootstrap_rerun=none" /tmp/iop-field-evidence/reconnect-pids.txt
python3 - <<'"'"'PY'"'"'
from pathlib import Path
p = Path("/tmp/iop-field-evidence/refresh-pid.txt").read_text()
parts = dict(item.split("=", 1) for item in p.split() if "=" in item)
assert parts["edge_pid_before"] == parts["edge_pid_after"], p
lines = Path("/tmp/iop-field-evidence/refresh-concurrent-4.jsonl").read_text().splitlines()
assert len(lines) == 4, len(lines)
print("field_evidence_ok lines=4")
PY
printf "FINAL_VERIFICATION=PASS field-reconnect field-refresh docs evidence=/tmp/iop-field-evidence review_root=/Users/toki/agent-work/iop-field-review\n"
'
```

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