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

# Code Review Reference - REVIEW_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=3, tag=REVIEW_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 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`
- Archived review logs:
  - `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. `plan_cloud_G08_2.log` and `code_review_cloud_G08_2.log` were superseded before implementation so they have no verdict.
- Required summary:
  - S16 `field-reconnect` evidence is still missing: `reconnect-after-status.json`, `reconnect-after-tcp.txt`, and `reconnect-pids.txt` were missing or empty.
  - S17 `field-refresh` evidence is still missing: remote `edge config` exposed only `check/init/print`, and `refresh-apply.json`, `refresh-pid.txt`, `refresh-concurrent-4.jsonl` were missing.
  - Remote `/Users/toki/agent-work/iop-dev` is dirty/divergent and lacks `apps/edge/internal/bootstrap/refresh_admin.go`; do not reset, clean, pull, or overwrite it.
  - Reviewer repaired dev-runtime Edge id wording in `docs/edge-local-dev-guide.md` and ignored local test profile `agent-test/local/edge-smoke.md` from `edge-toki-labs` to `edge-toki-labs-dev`.
- Fresh preflight facts baked into plan:
  - Local `HEAD=695df72`.
  - Remote runner `Darwin arm64`.
  - Remote `/Users/toki/agent-work/iop-dev` `HEAD=7398f32`, `branch=main`, `dirty_count=87`.
  - Existing remote binary had only `edge config check/init/print`; `config refresh` was absent.
  - Remote Control Plane status for `edge-toki-labs-dev` listed only `mac-codex-node,onexplayer-lemonade-node`.
  - GX10 host `192.168.0.91` was reachable as `Linux aarch64`; `go` was not found in PATH.
- 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 six 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_REVIEW_TEST-1] Read-Only Test Environment Preflight | [x] |
| [REVIEW_REVIEW_TEST-2] Clean Remote Dev-Runtime Rebuild | [x] |
| [REVIEW_REVIEW_TEST-3] Field Reconnect And Refresh Evidence | [ ] (GX10 SSH 접근 차단으로 미수집, 아래 `계획 대비 변경 사항` 참조) |

## 구현 체크리스트

- [x] `REVIEW_REVIEW_TEST-1` read-only 테스트 환경 프리플라이트를 파일 증거로 저장한다. 검증: local runtime source cleanliness, remote stale/dirty checkout facts, remote port/runtime facts, GX10 OS/arch facts가 `/tmp/iop-field-evidence/preflight-*.txt`에 남는다. (GX10은 reachable이 아니라 SSH publickey 거부 사실을 `preflight-gx10.txt`에 기록)
- [x] `REVIEW_REVIEW_TEST-2` 원격 dirty checkout을 보존하면서 clean review workdir를 만들고 현재 코드로 dev-runtime binaries를 재빌드한다. 검증: remote `edge config refresh --help`가 `--mode`를 노출하고 `build/dev-runtime/bin/edge config check`가 통과한다.
- [ ] `REVIEW_REVIEW_TEST-3` clean dev-runtime으로 S16/S17 field evidence를 수집한다. 검증: Edge restart 후 3 Node reconnect evidence, refresh dry-run/apply JSON, Edge PID 유지, `/healthz`, `/v1/models`, 4-line concurrent JSONL이 모두 존재한다. **GX10(`192.168.0.91`) SSH publickey 인증이 차단되어 `gx10-vllm-node` 기동과 GX10 vLLM이 서빙하는 `qwen3.6:35b` 검증이 불가하므로 미수집. 운영 중 dev-runtime을 파괴적으로 재기동하지 않기 위해 blind 실행하지 않음.**
- [x] 최종 검증 명령을 모두 실행하고 실제 stdout/stderr를 `CODE_REVIEW-cloud-G08.md`에 붙여 넣는다. (최종 ssh 블록은 reconnect/refresh evidence 부재 지점에서 exit 1)
- [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로 이동한다.

## 계획 대비 변경 사항

- **Local HEAD가 plan baked 값과 다름**: plan/CODE_REVIEW에 baked된 `local_head=695df72`와 달리 실제 local `HEAD=e3369a2`였다. 이는 plan 작성 이후 `e3369a2 sync: to agentic-framework v1.1.154` 커밋이 추가됐기 때문이다. runtime/build source path는 clean(`runtime_source_dirty=no`)이라 `git archive HEAD` 계약은 그대로 유효하다. 검증 명령은 변경하지 않았다.
- **GX10(`192.168.0.91`) SSH 접근 차단 — REVIEW_REVIEW_TEST-3 미수집 (외부 환경/접근 blocker)**: plan baked preflight는 GX10을 `Linux aarch64` reachable로 기록했으나, 이번 실행에서는 원격 runner에서 GX10으로의 SSH가 `Permission denied (publickey,password)`로 거부됐다. 진단 결과 GX10은 ping 정상, host key는 known/매치, 그러나 runner의 `id_ed25519`(SHA256:knRyVFvy0f5hAn5rNsWfc4E/ELn24hbrPQlC3MZ2tHg) 공개키를 GX10의 authorized_keys가 더 이상 수용하지 않는다. `toki`/`ubuntu`/`nvidia` 사용자 모두 동일하게 거부됐다. GX10 키 등록은 GX10 로그인이 선행되어야 하므로 runner에서는 복구할 수 없다.
  - 영향: REVIEW_REVIEW_TEST-3의 핵심 계약인 (a) Edge restart 후 `gx10-vllm-node` 포함 3 Node reconnect(S16), (b) GX10 vLLM이 서빙하는 `qwen3.6:35b` 동시 4 요청(S17)을 충족할 수 없다.
  - 대응: plan의 `의존 관계 및 구현 순서`에 따라 blind retry하지 않았다. 또한 REVIEW_REVIEW_TEST-3 검증 명령은 실행 중 dev-runtime CP/Edge/Node를 먼저 kill하고 review-root 바이너리로 재기동하는데, GX10 없이는 어차피 계약을 못 채우면서 운영 중 dev-runtime(포트 18083 등에서 서빙 중)만 파괴적으로 교체하게 되므로 실행하지 않았다. `reconnect-*`, `refresh-*`, `refresh-concurrent-4.jsonl` evidence는 미생성 상태이며 증거를 위조하지 않았다.
  - 분류: 이는 외부 환경/서비스 접근 blocker로, plan과 CODE_REVIEW 가이드상 user-review gate가 아니라 follow-up 사항이다. 따라서 `사용자 리뷰 요청`은 `없음`으로 둔다.
- **최종 검증 결과**: local 부분(`git diff --check`, docs drift rg)은 실행했고, 원격 ssh 최종 블록은 `set -e` 하에서 `test -s reconnect-before-status.json` 부재 지점에서 exit 1로 중단됐다(`FINAL_REMOTE_EXIT=1`). 참고로 최종 블록은 `preflight-local.txt`를 원격 `/tmp`에서 찾지만 해당 파일은 local runner에서 생성되며, 어차피 그 이전 reconnect/refresh evidence 부재로 실패한다.

## 주요 설계 결정

- **REVIEW_REVIEW_TEST-2를 GX10 차단과 독립적으로 완료**: clean review workdir(`/Users/toki/agent-work/iop-field-review`) 재빌드는 GX10/실행 중 서비스에 의존하지 않고 비파괴적(`config check`, `config refresh --help`는 offline CLI)이므로, 이전 loop의 가장 큰 미해결 gap이었던 "stale binary에 `config refresh` 부재"를 current-code 빌드로 명확히 해소했다. `refresh_admin.go` 존재, `config check` PASS, `config refresh`의 `--mode`(dry-run/apply)/`--addr`/`--config-path` 노출을 모두 증거로 남겼다.
- **원격 dirty checkout 보호 준수**: `/Users/toki/agent-work/iop-dev`는 reset/clean/pull/overwrite 없이 `*.yaml`만 복사했다. clean source는 별도 `/Users/toki/agent-work/iop-field-review`에 `git archive HEAD` tar stream으로 전개했다.
- **S18 docs 일관성 재확인**: `docs/edge-local-dev-guide.md`의 일반 field guide 섹션(port 18000)은 의도적으로 `edge-toki-labs`를, dev-runtime provider pool 섹션(102행 이하, port 18001)은 `edge-toki-labs-dev`를 사용한다. `agent-test/local/edge-smoke.md:118`이 이 구분을 명시하므로 final-verification rg의 `edge-toki-labs` 매치는 drift가 아니라 문서화된 구분이다(rg는 `|| true` 정보성). 단, S16/S17 field evidence가 미수집이라 docs와의 최종 cross-check는 GX10 복구 후 follow-up에서 닫아야 한다.
- **secret 보호 준수**: token/API key/private endpoint 원문을 출력/기록하지 않았다.

## 사용자 리뷰 요청

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

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

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

- 구현이 remote mutation 전에 `preflight-local.txt`, `preflight-remote.txt`, `preflight-gx10.txt`를 먼저 남겼는지 확인한다.
- 기존 원격 `/Users/toki/agent-work/iop-dev` dirty checkout을 reset/overwrite하지 않았는지 확인한다.
- clean review workdir binary가 현재 코드의 `config refresh` 명령과 refresh admin listener를 포함하는지 확인한다.
- `field-reconnect` evidence가 bootstrap 재실행 없이 Edge restart 후 3 Node 회복을 보여주는지 확인한다.
- `field-refresh` evidence가 refresh apply, Edge PID 유지, `/healthz`, `/v1/models`, 4 concurrent success를 모두 포함하는지 확인한다.
- 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_REVIEW_TEST-1 중간 검증

```bash
$ {
  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 -u
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"
  test -f apps/edge/internal/bootstrap/refresh_admin.go && echo "refresh_admin_source=yes" || echo "refresh_admin_source=no"
  if test -x build/dev-runtime/bin/edge; then
    echo "edge_config_commands="
    build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config --help 2>&1 | sed -n "/Available Commands:/,/Flags:/p"
    echo "edge_config_refresh_help="
    build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help 2>&1 || true
  fi
  grep -n "^refresh:" build/dev-runtime/edge.yaml 2>/dev/null || 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
  curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status 2>/dev/null | python3 -c "import sys,json; data=json.load(sys.stdin); print(\"edge_status_nodes=\" + \",\".join(n.get(\"id\", \"\") for n in data.get(\"nodes\", [])))" || true
} 2>&1 | tee /tmp/iop-field-evidence/preflight-remote.txt'
$ ssh toki@toki-labs.com 'set -u
GX10_HOST=192.168.0.91
ssh 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'

# --- preflight-local.txt (local runner /tmp/iop-field-evidence/preflight-local.txt, exit 0) ---
local_head=e3369a2
## main...origin/main
 M agent-roadmap/phase/update-plane-self-update-foundation/milestones/runtime-reconnect-config-refresh.md
 M agent-roadmap/sdd/update-plane-self-update-foundation/runtime-reconnect-config-refresh/SDD.md
 M apps/edge/README.md
 M docs/edge-local-dev-guide.md
?? agent-task/m-runtime-reconnect-config-refresh/
runtime_source_dirty=no
# (NOTE) plan baked local_head=695df72; 실제 e3369a2 (sync commit e3369a2 추가됨). runtime source clean.

# --- preflight-remote.txt (/tmp/iop-field-evidence/preflight-remote.txt) ---
runner_uname=Darwin arm64
remote_head=7398f32
remote_branch=main
remote_dirty_count=87
edge_bin=yes
refresh_admin_source=no
edge_config_commands=
Available Commands:
  check       Validate edge config can be loaded
  init        Write an edge.yaml template
  print       Print effective edge config as YAML

Flags:
edge_config_refresh_help=
Create, inspect, and validate the bundle-local edge.yaml configuration.
... (stale binary: config has only check/init/print; refresh subcommand 없음) ...
Available Commands:
  check       Validate edge config can be loaded
  init        Write an edge.yaml template
  print       Print effective edge config as YAML
27:refresh:
port_18001=control-p:41326
port_18083=edge:41330
port_18084=edge:41330,edge:41330,edge:41330,iop-node:41332,iop-node:41336
port_19093=edge_status_nodes=,
# (NOTE) stale dirty checkout 확인: HEAD 7398f32, dirty 87, refresh_admin.go 없음, config refresh 없음. 운영 중 dev-runtime이 18001/18083/18084 점유.

# --- preflight-gx10.txt (/tmp/iop-field-evidence/preflight-gx10.txt) ---
# 1차 시도 실제 출력:
Permission denied, please try again.
Permission denied, please try again.
toki@192.168.0.91: Permission denied (publickey,password).
# 진단: ping OK, host key known/matches, runner id_ed25519(SHA256:knRyVFvy0f5hAn5rNsWfc4E/ELn24hbrPQlC3MZ2tHg) GX10 authorized_keys에서 거부. toki/ubuntu/nvidia 모두 거부.
# 기록 파일 내용:
gx10_uname=UNREACHABLE_FROM_RUNNER
gx10_arch=UNKNOWN
gx10_go=UNKNOWN
gx10_ssh=Permission denied (publickey,password); host 192.168.0.91 ping OK, host key known/matches, runner id_ed25519 rejected by GX10 authorized_keys
note=Differs from plan baked preflight where GX10 was reachable as Linux aarch64. External access blocker; cannot be fixed from runner.
```

### REVIEW_REVIEW_TEST-2 중간 검증

```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
mkdir -p /tmp/iop-field-evidence
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
cp "$SOURCE_RUNTIME"/*.yaml build/dev-runtime/
test -f apps/edge/internal/bootstrap/refresh_admin.go
/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
printf "review_root=%s\n" "$REVIEW_ROOT"
'

# --- 실제 출력 (exit 0) ---
source_clean=yes
refresh_admin_source=yes
OK build/dev-runtime/edge.yaml
Send a dry-run or apply config refresh request to the Edge-local admin API.
The running Edge process must have refresh.enabled=true in its config.

Usage:
  edge config refresh [flags]

Examples:
  iop-edge config refresh --mode dry-run
  iop-edge config refresh --mode apply --config /path/to/edge.yaml

Flags:
      --addr string          refresh admin server address (default "127.0.0.1:19093")
      --config-path string   candidate config path (defaults to the resolved --config path)
  -h, --help                 help for refresh
      --mode string          refresh mode: dry-run or apply (default "dry-run")

Global Flags:
  -c, --config string   config file path (defaults to bundle-local edge.yaml)
mode_flag=present
review_root=/Users/toki/agent-work/iop-field-review

# review-config-check.txt: "OK build/dev-runtime/edge.yaml"
# review-config-refresh-help.txt: 위 refresh --help 전문(--mode 포함)
# => current-code clean 빌드는 config refresh --mode(dry-run/apply) 노출, config check PASS, refresh_admin.go 존재. 이전 loop의 stale-binary gap 해소.
```

### REVIEW_REVIEW_TEST-3 중간 검증

```bash
$ ssh toki@toki-labs.com 'set -euo pipefail
REVIEW_ROOT=/Users/toki/agent-work/iop-field-review
cd "$REVIEW_ROOT"
mkdir -p /tmp/iop-field-evidence build/dev-runtime/logs
{
  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@toki-labs.com 'set -euo pipefail
REVIEW_ROOT=/Users/toki/agent-work/iop-field-review
GX10_HOST=192.168.0.91
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"
'
$ ssh toki@toki-labs.com 'set -euo pipefail
EDGE_ID=edge-toki-labs-dev
REVIEW_ROOT=/Users/toki/agent-work/iop-field-review
cd "$REVIEW_ROOT"
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 || ss -tn state established "( sport = :18084 )" 2>/dev/null || true) | 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 || ss -tn state established "( sport = :18084 )" 2>/dev/null || true) | 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
'

# --- 미실행 (GX10 SSH 접근 차단으로 인한 blocker) ---
# 이 단계는 (a) review-root 바이너리로 CP/Edge/mac-codex/onexplayer Node 재기동, (b) GX10에 Linux Node scp/기동,
# (c) Edge restart 후 gx10-vllm-node 포함 3 Node reconnect, (d) refresh apply + qwen3.6:35b 동시 4 요청을 요구한다.
# GX10(192.168.0.91) SSH publickey 인증이 거부되어 (b)가 불가하고, 그 결과 (c) 3-node reconnect와
# (d) GX10 vLLM이 서빙하는 qwen3.6:35b 동시 요청을 충족할 수 없다.
# 이 명령의 첫 단계는 실행 중 dev-runtime CP/Edge/Node를 kill하고 review-root 바이너리로 교체하는데,
# GX10 없이는 계약을 못 채우면서 운영 중 서비스(18083 등)만 파괴적으로 교체하게 되므로 blind 실행하지 않았다.
# 생성되지 못한 evidence: reconnect-before-status.json(0바이트 기존 파일), 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. 증거를 위조하지 않았다.
# 상세 사유는 `계획 대비 변경 사항` 참조.
```

### 최종 검증

```bash
$ git diff --check
(exit 0, no output)

$ rg -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
docs/edge-local-dev-guide.md:29:  id: "edge-toki-labs"
docs/edge-local-dev-guide.md:99:curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status
agent-test/local/edge-smoke.md:55:  id: "edge-toki-labs"
agent-test/local/edge-smoke.md:118:- dev-runtime Edge id: `edge-toki-labs-dev` (... 일반 field guide의 `edge-toki-labs`와 구분한다.)
# (그 외 매치는 PLAN/CODE_REVIEW 본문의 패턴 인용 라인) 
# 판정: 위 edge-toki-labs 매치는 일반 field guide(port 18000) 정상 사용이며 drift 아님. dev-runtime 섹션(port 18001)은 edge-toki-labs-dev 사용.

$ ssh toki@toki-labs.com 'set -euo pipefail
for f in preflight-local.txt 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"
'
# --- 실제 출력 ---
FINAL_REMOTE_EXIT=1
# set -e 하에서 `test -s` 루프가 reconnect-before-status.json(0바이트)/이후 미생성 evidence 부재로 첫 실패 지점에서 중단.
# FINAL_VERIFICATION=PASS 라인은 출력되지 않음. field-reconnect/field-refresh evidence 미수집(REVIEW_REVIEW_TEST-3 blocker)을 정직히 반영.
# 통과한 부분: review-config-refresh-help.txt의 --mode 존재, preflight-remote/gx10/review-config-* 파일 생성.
```

## 코드리뷰 결과

_리뷰 에이전트 전용. 구현 에이전트는 이 섹션을 수정하지 않는다._

### 코드리뷰 결과 - 2026-06-23

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail - S16/S17 field behavior가 실제 GX10 포함 3-node reconnect와 4개 동시 요청으로 증명되지 않았다.
  - Completeness: Fail - `REVIEW_REVIEW_TEST-3`와 Roadmap Targets `field-reconnect`, `field-refresh`가 미완료다.
  - Test coverage: Fail - SDD Evidence Map S16/S17 필수 field evidence가 없다.
  - API contract: Pass - OpenAI-compatible request shape이나 public contract 변경은 없고, 문서의 `model` 사용은 기존 계약과 충돌하지 않는다.
  - Code quality: Pass - 이번 루프의 tracked source/docs 변경에서 debug artifact나 secret 노출은 확인되지 않았다.
  - Implementation deviation: Fail - 계획의 핵심 field smoke 단계가 GX10 SSH 차단으로 미실행됐다.
  - Verification trust: Fail - 최종 원격 검증은 required evidence 부재와 함께 local-only `preflight-local.txt`를 원격 `/tmp`에서 찾는 위치 mismatch가 있다.
  - Spec conformance: Fail - SDD S16/S17 완료 evidence가 충족되지 않았다.
- 발견된 문제:
  - Required: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_3.log:76` / `:82` / `:392` - SDD S16/S17 필수 evidence가 없다. GX10 SSH 접근을 복구한 뒤 clean review workdir 기준으로 3개 Node reconnect status/TCP, `bootstrap_rerun=none` PID evidence, refresh dry-run/apply, Edge PID 유지, `/healthz`, `/v1/models`, `refresh-concurrent-4.jsonl` 4 lines를 수집해야 한다.
  - Required: `agent-task/m-runtime-reconnect-config-refresh/07+01,03,04,05,06_field_docs_smoke/code_review_cloud_G08_3.log:419` - 최종 원격 검증이 local에서 생성한 `/tmp/iop-field-evidence/preflight-local.txt`를 원격에서 검사한다. 실제 read-only 재확인에서도 원격 `preflight-local.txt`는 missing이었다. 후속 검증은 local preflight 파일 검사를 local command로 분리하고, 원격 command는 원격 evidence만 검사하도록 고쳐야 한다.
- 다음 단계: FAIL follow-up plan/review를 작성한다. USER_REVIEW gate는 트리거하지 않는다.
