<!-- task=m-agent-readable-repository-refactor/15+14_runtime_full_cycle plan=0 tag=TEST -->

# Code Review Reference - 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-07-18
task=m-agent-readable-repository-refactor/15+14_runtime_full_cycle, plan=0, tag=TEST

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- Milestone link: [Milestone 문서](../../../agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md)
- Task ids:
  - `regression`: 전체 Go/Flutter 회귀와 관련 E2E/full-cycle 검증
- Completion mode: check-on-pass

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

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

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

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G08.md`와 `PLAN-cloud-G08.md`를 numbered log로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/15+14_runtime_full_cycle/`로 이동한다.
4. PASS이면 runtime이 읽을 m-task 완료 이벤트를 보고하며 roadmap 수정은 하지 않는다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [TEST-1] Deterministic actual-process full-cycle | [x] |
| [TEST-2] Real external CLI profile matrix | [x] |

## 구현 체크리스트

- [x] `14_repository_regression`의 active 또는 archive `complete.log`를 정확히 하나 확인하고 HEAD/evidence를 기록한다.
- [x] runtime 도구·CLI·로그인/config/port preflight를 실행하고 profile별 가용 상태를 기록한다.
- [x] mock Edge-Node 실제 process full-cycle을 실행해 register, foreground message 2회, background run, payload identity/order, `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/terminate-session`을 확인한다.
- [x] `make test-e2e`를 실행해 OpenAI-compatible Ollama와 Control Plane-Edge wire 보조 smoke까지 확인한다.
- [x] `claude`, `antigravity(agy)`, `codex`, `opencode`를 임시 config로 각각 실제 호출하고 같은 session message 2회, start/message/complete, 지원 command/status를 profile별로 기록한다. 실행 불가 profile은 실제 blocker evidence를 남기고 PASS로 처리하지 않는다.
- [x] 검증 전후 HEAD/dirty state와 common 파일 무변경을 확인하고 최종 PASS 또는 남은 blocker를 명확히 기록한다.
- [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을 확인한다.
- [x] PASS이면 complete log template으로 `complete.log`를 작성하고 active `.md`를 남기지 않는다.
- [x] PASS이면 task directory를 archive하고 최종 archive 위치에서 체크리스트를 갱신한다.
- [x] PASS이면 m-task 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent를 제거하거나 남은 sibling을 확인한다.
- [ ] WARN/FAIL이면 다음 active Plan/Review를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 선택된 Milestone 잠금 결정 근거가 실제로 있는지 확인한다.

## 계획 대비 변경 사항

1. **선행 evidence HEAD 불일치에 따른 repository regression 재실행 (추가 실행)**
   - Plan 프리플라이트는 HEAD `be05139b5be6a7938ac102aaf0664ed08e4cebe3`를 기록했으나 실행 시점 HEAD는 `9a4013904d084646784c1579b13f6793b75c1204`였다. `be05139`는 HEAD의 ancestor이며 그 사이 commit `9a40139`가 `apps/edge/internal/transport/server.go`, `apps/node/internal/transport/client.go` 등 production source를 포함했다.
   - Plan의 `source sync` 규칙("HEAD가 달라졌으면 repository regression을 재실행하거나 blocker로 기록한다")에 따라 blocker로 남기는 대신 `14_repository_regression`의 결정적 회귀(Go/Flutter/readability)를 현재 HEAD에서 재실행했고 전부 통과했다. 결과는 `검증 결과 > 선행 evidence 재검증`에 있다.

2. **TEST-2 실행 경로를 `scripts/e2e-smoke.sh` real mode가 아닌 `scripts/dev/*` 직접 구동으로 확정**
   - `scripts/e2e-smoke.sh`의 real mode는 `$REPO_ROOT/configs/edge.yaml`을 직접 읽고 legacy `adapters.cli.profiles` 블록만 추출한다(`scripts/e2e-smoke.sh:161-183`). tracked config는 provider-first 형식이라 해당 loader와 호환되지 않고, config 경로 override 환경변수도 없다. tracked config 수정 금지 범위를 지키기 위해 Plan TEST-2가 명시한 `scripts/dev/edge.sh` + `scripts/dev/node.sh` 직접 구동(`IOP_EDGE_CONFIG`/`IOP_NODE_CONFIG` override)을 사용했다.
   - 드라이버는 `/tmp/iop-agent-readable-runtime-full-cycle/run-real-profile.sh`이며 tracked 파일이 아니다.

3. **opencode profile 최초 실행 실패 후 환경 변수 격리로 재실행**
   - 최초 실행은 `cli adapter: start opencode server: timeout waiting for opencode server listen line`으로 실패했다. 직접 재현 결과 원인은 IOP 코드가 아니라 opencode CLI가 자기 데이터 디렉터리에 로그를 쓰지 못하는 것이었다(`PermissionDenied: FileSystem.open (/config/.local/share/opencode/log/opencode.log)`, 로그 `opencode-serve-direct.log`).
   - profile `env`에 `XDG_DATA_HOME=/tmp/iop-agent-readable-runtime-full-cycle/xdg`를 넣어 데이터 디렉터리만 `/tmp`로 격리한 뒤 재실행해 PASS했다. tracked config와 사용자 홈 설정은 수정하지 않았다.

4. **`/status`는 profile별 실제 결과를 그대로 기록**
   - `claude`만 status 본문이 출력됐다. `opencode`는 명시적 unsupported error, `antigravity`/`codex`는 TUI status probe startup 실패 error가 나왔다. 실패를 성공으로 처리하지 않고 `검증 결과`와 `남은 위험`에 원문으로 남겼다.

## 주요 설계 결정

- **임시 config 전략**: tracked `configs/edge.yaml`/`configs/node.yaml`을 읽기 참조로만 쓰고, profile별 `/tmp/iop-agent-readable-runtime-full-cycle/edge-<profile>.yaml`과 `node-<profile>.yaml`을 새로 생성했다. Edge listen/bootstrap/metrics와 Node metrics 포트는 loopback 동적 값(31000~55000 범위 랜덤)으로 두어 기존 9090/18081/19090 listener를 가정하지 않았다.
- **profile 정의 형식**: tracked config와 같은 provider-first(`nodes[].providers[]`) 형식을 사용했다. `apps/edge/internal/node/mapper.go:212-232`에서 `type: cli` provider의 `id`가 그대로 adapter instance key이자 CLI profile key로 컴파일되므로, legacy `adapters.cli.profiles` 대신 현재 운영 경로를 그대로 검증한다.
- **profile matrix 명령 형태**: `apps/node/README.md`와 `apps/edge/README.md`의 문서화된 non-interactive 조합을 따랐다. `claude` = `claude -p --dangerously-skip-permissions` one-shot, `antigravity` = `agy --dangerously-skip-permissions --print` + `resume_args`(`--conversation`) 기반 `antigravity-print`, `codex` = `codex app-server` 기반 `codex-app-server`, `opencode` = `opencode-sse`.
- **evidence sanitization**: node/edge 로그와 `/status` 출력에서 token/secret 원문은 기록하지 않았다. Node token은 검증 전용 임시 값(`g08-local-token`)이며 실제 운영 token이 아니다. `claude` `/status` 출력에는 계정 식별자나 raw credential이 없어 login_method/model 라인까지 그대로 남겼다.
- **실패 처리**: 드라이버는 개별 검증 실패 시 즉시 종료하지 않고 이후 command까지 실행한 뒤 profile 단위 FAIL을 반환하도록 했다. blind retry는 하지 않고 실패한 profile은 원인 진단 후 1회만 재실행했다(opencode).

## 사용자 리뷰 요청

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

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

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

- predecessor `complete.log`가 정확히 하나이고 같은 source state를 증명하는지 확인한다.
- mock smoke를 full-cycle/real CLI evidence와 혼동하지 않았는지 확인한다.
- node local payload와 edge rendered payload의 내용·순서 동일성 및 complete ordering을 확인한다.
- real CLI 네 profile이 각각 message 2회와 command/status evidence를 가지는지 확인한다.
- blocker profile이 있는데 PASS로 판정하지 않았는지 확인한다.
- tracked config와 common 파일이 수정되지 않았는지 확인한다.

## 검증 결과

_구현 에이전트가 각 명령의 실제 stdout/stderr 또는 원본 로그 경로를 기록한다._

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

### 선행 evidence 확인과 프리플라이트

```text
$ find agent-task/archive -path '*m-agent-readable-repository-refactor/14_repository_regression/complete.log'
agent-task/archive/2026/07/m-agent-readable-repository-refactor/14_repository_regression/complete.log
(정확히 1개. active `agent-task/m-agent-readable-repository-refactor/`에는 15+14_runtime_full_cycle만 존재)

선행 complete.log 기록 HEAD: be05139b5be6a7938ac102aaf0664ed08e4cebe3

$ git rev-parse HEAD
9a4013904d084646784c1579b13f6793b75c1204

$ git status --short
(빈 출력 = clean worktree)

$ git merge-base --is-ancestor be05139b5be6a7938ac102aaf0664ed08e4cebe3 HEAD && echo "be05139 is ancestor of HEAD"
be05139 is ancestor of HEAD

$ git diff --stat be05139b5be6a7938ac102aaf0664ed08e4cebe3 HEAD | tail -20
 .../CODE_REVIEW-cloud-G08.md                       | 174 +++++
 .../15+14_runtime_full_cycle/PLAN-cloud-G08.md     | 225 +++++++
 agent-test/README.md                               |   1 +
 agent-test/dev/edge-smoke.md                       |  25 +-
 agent-test/dev/inventory.yaml                      | 272 +++++++-
 agent-test/dev/node-smoke.md                       |  22 +-
 agent-test/dev/rules.md                            |  12 +-
 apps/edge/internal/transport/heartbeat_test.go     |   9 +
 apps/edge/internal/transport/server.go             |   4 +-
 apps/node/internal/transport/client.go             |   4 +-
 apps/node/internal/transport/heartbeat_test.go     |   9 +
 scripts/inventory-query/main.go                    | 423 ++++++++++++
 scripts/inventory-query/main_test.go               | 735 +++++++++++++++++++++
 scripts/inventory-query/provider_query.go          | 172 +++++
 scripts/inventory-query/provider_query_test.go     | 135 ++++
 scripts/readability_audit.py                       |   7 +-
 scripts/readability_audit_test.py                  |   3 +
 scripts/readability_baseline.json                  |  31 +-
 scripts/readability_read_sets.json                 |  68 ++
 51 files changed, 7342 insertions(+), 139 deletions(-)

=> HEAD가 선행 evidence와 다르고 production source를 포함하므로 아래 재검증을 실행했다.

$ for c in go flutter make bash curl timeout claude agy codex opencode shellcheck; do printf '%s=' "$c"; command -v "$c" || echo "MISSING"; done
go=/config/.local/bin/go
flutter=/sdk/flutter/bin/flutter
make=/config/.local/bin/make
bash=/bin/bash
curl=/bin/curl
timeout=/bin/timeout
claude=/config/.npm-global/bin/claude
agy=/config/.local/bin/agy
codex=/config/.npm-global/bin/codex
opencode=/config/.npm-global/bin/opencode
shellcheck=MISSING

$ go version; flutter --version | head -1; uname -s -m
go version go1.26.2 linux/arm64
Flutter 3.41.5 • channel stable • https://github.com/flutter/flutter.git
Linux aarch64
```

runner/workdir: local runner, `/config/workspace/iop`, branch `main`. `shellcheck`는 없으며 `scripts/e2e-smoke.sh`가 자체적으로 skip 처리한다(`[e2e] shellcheck not found, skipping`).

### 선행 evidence 재검증 (HEAD 9a40139 기준 repository regression 재실행)

```text
$ GOCACHE=/tmp/iop-agent-readable-runtime-full-cycle/go-cache go test -count=1 ./...
(원본 로그: /tmp/iop-agent-readable-runtime-full-cycle/go-test.log)
...
?   	iop/packages/go/policy	[no test files]
?   	iop/packages/go/version	[no test files]
?   	iop/proto/gen/iop	[no test files]
ok  	iop/scripts/inventory-query	0.008s
exit=0
(ok 34 package, FAIL 0)

$ make client-test
(원본 로그: /tmp/iop-agent-readable-runtime-full-cycle/client-test.log)
00:05 +42: All tests passed!
client-test exit=0

$ (cd apps/client && flutter analyze)
Analyzing client...
No issues found! (ran in 5.0s)
analyze exit=0

$ python3 -m unittest scripts.readability_audit_test
Ran 122 tests in 2.341s

OK
readability-unit exit=0

$ python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-agent-readable-runtime-full-cycle/readability.json
RATCHET OK: no new or increased violations.
readability-audit: 392 files, 107901 LOC, 3438 functions, 163 violations
readability-check exit=0
```

### TEST-1 중간 검증

```text
$ GOCACHE=/tmp/iop-agent-readable-runtime-full-cycle/go-cache IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh
(원본 로그: /tmp/iop-agent-readable-runtime-full-cycle/edge-node-mock.log)
[e2e] NOTE: auxiliary smoke only; completion requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
[e2e] shellcheck not found, skipping
...
[edge-message] 준비되었는지 묻는 짧은 테스트입니다. IOP_E2E_READY_SHORT 만 출력하세요.
[node-event] start run_id=manual-1784386223326648877
[node-message] IOP_E2E_READY_SHORT
IOP_E2E_READY_SHORT_TAIL
[node-event] complete run_id=manual-1784386223326648877 detail="idle-timeout"
...
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
exit=0

(스크립트가 실제 Edge/Node process로 검증한 항목: node registration, /nodes, /capabilities(targets = fake-cli),
 /transport(connected = true, state = connected, node_id/session_id/target), foreground message 2회의
 sent/start/[node0-msg]/complete, /session session2 + /background on 상태의 background run 1회,
 /background off, /sessions, /terminate-session. 각 wait 단계는 실패 시 즉시 exit 1이며 exit=0은 전 단계 통과를 뜻한다.)

$ GOCACHE=/tmp/iop-agent-readable-runtime-full-cycle/go-cache make test-e2e
(원본 로그: /tmp/iop-agent-readable-runtime-full-cycle/make-test-e2e.log)
119:[e2e] Auxiliary smoke test PASSED.
122:[openai-ollama] OpenAI-compatible Ollama serving test PASSED.
168:[cp-edge-wire] Control Plane-Edge wire smoke PASSED.
{"level":"info","ts":1784386250.353085,"logger":"controlplane","caller":"controlplane/connector.go:280","msg":"connected to control plane","wire_addr":"127.0.0.1:31704","protocol":"protobuf-socket"}
===========================
[cp-edge-wire] Control Plane-Edge wire smoke PASSED.
exit=0
```

### TEST-2 중간 검증

```text
$ for cmd in claude agy codex opencode; do command -v "$cmd"; done
/config/.npm-global/bin/claude
/config/.local/bin/agy
/config/.npm-global/bin/codex
/config/.npm-global/bin/opencode

$ claude --version; agy --version; codex --version; opencode --version
2.1.212 (Claude Code)
1.0.16
codex-cli 0.144.5
1.18.3

$ timeout 120 claude -p --dangerously-skip-permissions "Reply with exactly: IOP_PREFLIGHT_CLAUDE_OK"
IOP_PREFLIGHT_CLAUDE_OK
exit=0

$ timeout 150 agy --dangerously-skip-permissions --print "Reply with exactly: IOP_PREFLIGHT_AGY_OK"
IOP_PREFLIGHT_AGY_OK
exit=0

(로그인/provider preflight: claude, agy는 위 one-shot 호출로 확인. codex, opencode는 아래 profile 구동에서
 실제 응답으로 확인했다.)

profile=claude
$ IOP_EDGE_CONFIG=/tmp/.../edge-claude.yaml scripts/dev/edge.sh + IOP_NODE_CONFIG=/tmp/.../node-claude.yaml scripts/dev/node.sh
(드라이버: /tmp/iop-agent-readable-runtime-full-cycle/run-real-profile.sh claude,
 로그: profile-claude.log / edge-claude.out / node-claude.out)
[g08] profile=claude port=33353 persistent=0 run_timeout=420s
edge> [node0-evt] connected reason="registered"
edge> [node0-capabilities] adapter=cli target=claude session=default
  adapter = cli
  targets = claude
edge> [node0-transport] adapter=cli target=claude session=default
  connected = true
  state = connected
  node_id = test-node
  session_id = default
  target = claude
[node0-evt] start run_id=manual-1784386335763301846
[node0-msg] IOP_G08_CLAUDE_ONE
[node0-evt] complete run_id=manual-1784386335763301846 detail="cli execution complete"
[node0-evt] start run_id=manual-1784386339328728666
[node0-msg] IOP_G08_CLAUDE_TWO
[node0-evt] complete run_id=manual-1784386339328728666 detail="cli execution complete"
edge> [node0-sessions] adapter=cli target=claude session=default
  count = 0
  sessions =
[node0-status] adapter=cli target=claude session=default
Claude status:
  version = 2.1.212
  login_method = Claude Pro account
  model = opus (claude-opus-4-8)
  cwd = /tmp/iop-agent-readable-runtime-full-cycle
  session_id = 52ec8691-7c52-46b2-bf4c-3c45bee20217
[g08] profile=claude result=PASS
exit=0

node local payload(node-claude.out):
[node-event] start run_id=manual-1784386335763301846
[node-message] IOP_G08_CLAUDE_ONE
[node-event] complete run_id=manual-1784386335763301846 detail="cli execution complete"
[node-event] start run_id=manual-1784386339328728666
[node-message] IOP_G08_CLAUDE_TWO
[node-event] complete run_id=manual-1784386339328728666 detail="cli execution complete"
=> edge rendered payload와 run_id/내용/순서 동일. complete는 각 run의 마지막 message 이후에만 나타난다.
=> one-shot profile이므로 persistent session이 남지 않고 /sessions count = 0이 정상이다. /terminate-session은 미실행.

profile=antigravity
(드라이버: run-real-profile.sh antigravity, 로그: profile-antigravity.log / edge-antigravity.out / node-antigravity.out)
[g08] profile=antigravity port=32846 persistent=1 run_timeout=420s
edge> [node0-evt] connected reason="registered"
[node0-evt] start run_id=manual-1784386363638650928
[node0-msg] IOP_G08_ANTIGRAVITY_ONE
[node0-evt] complete run_id=manual-1784386363638650928 detail="cli execution complete"
[node0-evt] start run_id=manual-1784386371758436959
[node0-msg] IOP_G08_ANTIGRAVITY_TWO
[node0-evt] complete run_id=manual-1784386371758436959 detail="cli execution complete"
edge> [node0-sessions] adapter=cli target=antigravity session=default
sessions: 1
  [0] mode=antigravity-print target=antigravity session=default
edge> terminated session default node=node0
edge> [edge] sent command=status node=node0 adapter=cli target=antigravity session=default
error: node reported error: antigravity startup failed: timeout waiting for for shortcuts|Type your message
[g08] profile=antigravity result=PASS
exit=0

node local payload(node-antigravity.out): run_id/내용/순서 edge와 동일.
=> message 2회, /sessions(antigravity-print conversation session 1개), /terminate-session 성공.
=> /status는 명시적 error로 종료(아래 남은 위험 참고). 성공으로 처리하지 않았다.

profile=codex
(드라이버: run-real-profile.sh codex, 로그: profile-codex.log / edge-codex.out / node-codex.out)
[g08] profile=codex port=34916 persistent=1 run_timeout=420s
edge> [node0-evt] connected reason="registered"
[node0-evt] start run_id=manual-1784386420409466760
[node0-msg] IOP_G08_CODEX_ONE
[node0-evt] complete run_id=manual-1784386420409466760 detail=""
[node0-evt] start run_id=manual-1784386428062453055
[node0-msg] IOP_G08_CODEX_TWO
[node0-evt] complete run_id=manual-1784386428062453055 detail=""
edge> [node0-sessions] adapter=cli target=codex session=default
sessions: 1
  [0] mode=codex-app-server target=codex session=default
edge> terminated session default node=node0
edge> [edge] sent command=status node=node0 adapter=cli target=codex session=default
error: node reported error: codex startup failed after skipping update prompt: EOF
[g08] profile=codex result=PASS
exit=0

node local payload(node-codex.out): run_id/내용/순서 edge와 동일.
=> message 2회, codex-app-server session 유지, /terminate-session 성공.
=> /status는 명시적 error(아래 남은 위험 참고).

profile=opencode
(드라이버: run-real-profile.sh opencode, 로그: profile-opencode.log / edge-opencode.out / node-opencode.out)

1차 실행 = FAIL:
[node0-evt] error run_id=manual-1784386443983397340 detail="cli adapter: start opencode server: timeout waiting for opencode server listen line"
{"level":"warn","caller":"node/run_handler.go:248","msg":"run ended","status":"failed","error":"cli adapter: start opencode server: timeout waiting for opencode server listen line"}

원인 직접 재현(로그: opencode-serve-direct.log):
$ timeout 40 opencode serve --hostname 127.0.0.1 --port 0
Error: Unexpected error
PermissionDenied: FileSystem.open (/config/.local/share/opencode/log/opencode.log)
exit=1
=> IOP adapter가 아니라 opencode CLI가 자신의 데이터 디렉터리에 로그를 쓰지 못하는 runner 환경 문제.

데이터 디렉터리 격리 후 재현 확인(로그: opencode-serve-xdg.log):
$ timeout 45 env XDG_DATA_HOME=/tmp/iop-agent-readable-runtime-full-cycle/xdg opencode serve --hostname 127.0.0.1 --port 0
Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.
opencode server listening on http://127.0.0.1:4096

2차 실행(profile env에 XDG_DATA_HOME만 추가) = PASS:
[g08] profile=opencode port=34314 persistent=1 run_timeout=240s
edge> [node0-evt] connected reason="registered"
[node0-evt] start run_id=manual-1784387156292470461
[node0-msg] IOP_G08_OPENCODE_ONE
[node0-evt] complete run_id=manual-1784387156292470461 detail="opencode sse execution complete"
[node0-evt] start run_id=manual-1784387175698520429
[node0-msg] IOP_G08_OPENCODE_TWO
[node0-evt] complete run_id=manual-1784387175698520429 detail="opencode sse execution complete"
edge> [node0-sessions] adapter=cli target=opencode session=default
sessions: 1
  [0] mode=opencode-sse target=opencode session=default
edge> terminated session default node=node0
edge> [edge] sent command=status node=node0 adapter=cli target=opencode session=default
error: node reported error: status check not supported for target "opencode" (command "opencode")
[g08] profile=opencode result=PASS
exit=0

node local payload(node-opencode.out): run_id/내용/순서 edge와 동일.
=> message 2회, opencode-sse session 유지, /terminate-session 성공, /status는 명시적 unsupported error(기준 충족).
```

profile 요약:

| profile | command | mode | message x2 | payload identity/order | /nodes /capabilities /transport | /sessions | /terminate-session | /status |
|---------|---------|------|-----------|------------------------|---------------------------------|-----------|--------------------|---------|
| `claude` | `claude -p --dangerously-skip-permissions` | one-shot | PASS | PASS | PASS | PASS (count=0, one-shot 정상) | N/A (non-persistent) | PASS (본문 출력) |
| `antigravity` | `agy --dangerously-skip-permissions --print` (+ `--conversation` resume) | `antigravity-print` | PASS | PASS | PASS | PASS (session 1) | PASS | ERROR (startup timeout) |
| `codex` | `codex app-server` | `codex-app-server` | PASS | PASS | PASS | PASS (session 1) | PASS | ERROR (startup EOF) |
| `opencode` | `opencode` (`serve` 자동 기동) | `opencode-sse` | PASS | PASS | PASS | PASS (session 1) | PASS | 명시적 unsupported error |

### 최종 검증

```text
$ git rev-parse HEAD
9a4013904d084646784c1579b13f6793b75c1204
(검증 시작 시점 HEAD와 동일)

$ git status --short
(빈 출력 = tracked 파일 변경 없음. 이 리뷰 파일 작성 전 기준이며, 작성 후에는
 agent-task/m-agent-readable-repository-refactor/15+14_runtime_full_cycle/CODE_REVIEW-cloud-G08.md만 변경된다.)

$ git diff --check
(빈 출력, exit=0 — whitespace 오류 없음)

$ git diff --name-only | rg '^agent-ops/(rules|skills)/common/' && exit 1 || true
no common file diff

$ rg -n --sort path 'Auxiliary smoke test PASSED|OpenAI-compatible Ollama serving test PASSED|Control Plane-Edge wire smoke PASSED' /tmp/iop-agent-readable-runtime-full-cycle/*.log
/tmp/iop-agent-readable-runtime-full-cycle/edge-node-mock.log:117:[e2e] Auxiliary smoke test PASSED.
/tmp/iop-agent-readable-runtime-full-cycle/make-test-e2e.log:119:[e2e] Auxiliary smoke test PASSED.
/tmp/iop-agent-readable-runtime-full-cycle/make-test-e2e.log:122:[openai-ollama] OpenAI-compatible Ollama serving test PASSED.
/tmp/iop-agent-readable-runtime-full-cycle/make-test-e2e.log:168:[cp-edge-wire] Control Plane-Edge wire smoke PASSED.
```

### 최종 판단과 남은 위험

- deterministic runtime marker 3종 PASS, real CLI 4 profile 모두 실제 구동으로 same-session message 2회와 payload identity/order를 확인했다. tracked config/production source/common 파일은 수정하지 않았다.
- 남은 위험 1 — `antigravity`, `codex`의 `/status`: 두 profile 모두 usage status를 TUI probe로 확인하는 경로라 PTY가 없는 이번 non-interactive runner에서 startup 단계에서 실패했다(`antigravity startup failed: timeout waiting for for shortcuts|Type your message`, `codex startup failed after skipping update prompt: EOF`). message 실행 경로 자체는 두 profile 모두 정상이므로 실행 경로 회귀가 아니라 status probe의 실행 환경 제약으로 본다. 재현 명령: `IOP_G08_RUN_TIMEOUT=240 /tmp/iop-agent-readable-runtime-full-cycle/run-real-profile.sh <antigravity|codex>` 후 `edge-<profile>.out`의 `/status` 라인 확인. 이 Plan은 동작 변경 금지 범위이므로 여기서 고치지 않았고 follow-up 판단은 code-review에 맡긴다.
- 남은 위험 2 — `opencode`는 runner 홈 데이터 디렉터리(`/config/.local/share/opencode`)에 쓸 수 없어 profile `env`의 `XDG_DATA_HOME` 격리가 필요했다. tracked config에는 이 override가 없으므로 같은 제약이 있는 환경에서 tracked 설정 그대로 opencode profile을 쓰면 동일한 listen-line timeout이 재발한다.
- 남은 위험 3 — `shellcheck`가 runner에 없어 `scripts/e2e-smoke.sh`의 shellcheck 단계는 skip되었다(스크립트 자체 fallback).
- 선행 `14_repository_regression` evidence와 HEAD가 달라 해당 결정적 회귀를 현재 HEAD에서 재실행해 전부 통과시켰다. 별도 blocker는 남기지 않았다.

---

> **[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 | 구현 에이전트는 수정하거나 종결 절차를 실행하지 않는다 |
| Roadmap Targets | Fixed at stub creation | 구현 에이전트는 수정하지 않고 code-review가 PASS 시 complete.log로 복사한다 |
| 구현 항목별 완료 여부 | Implementing agent | 항목명은 고정하고 `[ ]`를 `[x]`로만 갱신한다 |
| 구현 체크리스트 | Implementing agent | 항목 text/order는 고정하고 checkbox만 갱신한다 |
| 코드리뷰 전용 체크리스트 | Review agent only | 구현 에이전트는 수정하지 않는다 |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | 실제 내용을 기록한다 |
| 사용자 리뷰 요청 | Implementing agent | Milestone 잠금 결정 blocker일 때만 갱신한다 |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | 구현 에이전트는 수정하지 않는다 |
| 검증 결과 | Implementing agent | command heading은 고정하고 실제 출력만 채운다 |
| 코드리뷰 결과 | Review agent appends | stub에는 포함하지 않는다 |

## 코드리뷰 결과

- 종합 판정: PASS
- 차원별 평가:
  - Correctness: Pass — 실제 네 CLI profile 모두 같은 `default` session에서 2회씩 `sent → start → message → complete` 순서를 지켰고, run id와 node-local/edge-rendered payload가 일치했다.
  - Completeness: Pass — 선행 `14_repository_regression` evidence를 정확히 하나 확인했고, 달라진 HEAD에서 전체 Go/Flutter/readability 회귀를 재실행했으며 mock/full-cycle, OpenAI-compatible Ollama, Control Plane-Edge wire 검증을 모두 기록했다.
  - Test coverage: Pass — `go test -count=1 ./...`, `make client-test`, `flutter analyze`, readability unit/ratchet, `scripts/e2e-smoke.sh`, `make test-e2e`, 실제 CLI 4종 profile evidence가 계획 범위를 충족한다.
  - API contract: Pass — tracked source/API/wire/config 변경 없이 Edge-Node 등록·실행 이벤트·command/session 계약과 Control Plane-Edge 보조 wire marker가 유지됐다.
  - Code quality: Pass — 이 subtask는 tracked production/test source를 수정하지 않았고, 임시 driver/config/log는 `/tmp`에 격리했다.
  - Implementation deviation: Pass — HEAD 불일치 시 회귀 재실행, provider-first 임시 config, opencode `XDG_DATA_HOME` 격리는 계획의 허용 범위와 근거에 맞게 기록됐다.
  - Verification trust: Pass — 리뷰 시 원본 로그를 다시 대조해 profile별 2회 relay, payload 동일성, session command, 필수 smoke marker, 종료 코드, predecessor 수와 현재 diff 범위를 기계적으로 확인했다.
- 발견된 문제: 없음
- 다음 단계: PASS — numbered plan/review log와 `complete.log`를 작성하고 task directory를 월별 archive로 이동한다.
