<!-- task=16_proto_socket_heartbeat_reproducibility plan=0 tag=PROTODEP -->

# Code Review Reference - PROTODEP

> **[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.
> Do not modify or check the `코드리뷰 전용 체크리스트`; it is owned by the review agent only.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-05-17
task=16_proto_socket_heartbeat_reproducibility, plan=0, tag=PROTODEP

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

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료 후 반드시 아래 순서로 아카이브하세요.

1. `CODE_REVIEW-cloud-G09.md` → `code_review_cloud_G09_N.log` (N = 기존 code_review_*.log 수)
2. `PLAN-cloud-G09.md` → `plan_cloud_G09_M.log` (M = 기존 plan_*.log 수)
3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 routed plan + review 스텁 작성.

어떤 판정에서도 아카이브를 건너뛰지 마세요. PASS/WARN/FAIL 모두 `코드리뷰 결과` append 후 active plan/review 파일을 먼저 아카이브하고, 그 다음 `complete.log` 또는 다음 plan/review 파일을 작성해야 합니다.
아카이브와 다음 상태 파일 작성이 끝난 뒤, archived `code_review_*.log`의 `코드리뷰 전용 체크리스트`를 모두 체크한 다음 보고하세요.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [PROTODEP-1] proto-socket 변경을 versioned clean 상태로 만들기 | [x] |
| [PROTODEP-2] go-iop dependency handoff 명확화 | [x] |
| [PROTODEP-3] 최종 acceptance 재검증 | [x] |

## 구현 체크리스트

- [x] [PROTODEP-1] `/config/workspace/proto-socket` heartbeat/disconnect-info 변경을 versioned clean 상태로 만든다.
- [x] [PROTODEP-2] go-iop가 dirty sibling dependency에 의존하지 않는다는 manifest/status 증거를 남긴다.
- [x] [PROTODEP-3] proto-socket tests, go-iop tests, mock e2e, real idle acceptance를 재검증한다.
- [x] 중간 검증과 최종 검증 명령을 모두 실행하고 실제 stdout/stderr를 CODE_REVIEW에 기록한다.
- [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_G09_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G09_M.log`로 아카이브한다.
- [x] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [x] PASS이면 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] WARN/FAIL이면 다음 active `PLAN-cloud-G09.md`와 `CODE_REVIEW-cloud-G09.md`를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- PROTODEP-2 중간 검증의 `git status --short -- iop.db` 첫 실행 시 ` D iop.db` 출력이 발생함. 원인은 직전 단계의 `go test ./apps/node/internal/transport`가 root에서 실행되며 tracked `iop.db` 파일을 삭제하기 때문으로, proto-socket dependency handoff와 무관한 사전 존재 이슈(commit `6e2a1fc`가 tracked한 iop.db가 `.gitignore`의 `/iop.db`와 충돌)임. plan 계약을 만족시키기 위해 검증 사이에 `git checkout HEAD -- iop.db`로 복구하고, 이후 mock/Gemini e2e의 before/after stat 비교는 정상적으로 일치(`1779007121 28672`)했다. 이 부수효과는 별도 후속 task에서 다룰 사안으로 본 작업 범위 밖.
- `go.mod`의 `replace ../proto-socket/go`는 plan 권고("기존 구조를 우선")에 따라 유지. proto-socket clean commit SHA `c9d21b9`를 본 review에 증거로 기록.

## 주요 설계 결정

- **proto-socket commit pinning**: dirty heartbeat/disconnect-info 변경을 단일 commit `c9d21b988805857c260ebeee4c708f82f1958212` (`feat: add DisconnectInfo and fix stale heartbeat wait-timer race`)로 versioning. proto-socket repo의 직전 HEAD는 `a4380da`였고, 이번 commit이 13번 task 완료 로그에서 기록된 모든 stale wait-timer fix, DisconnectInfo struct, paired-idle regression test를 단일 단위로 고정한다.
- **`go.mod` replace 유지**: standalone-module 빌드 목표가 명시되지 않았고, plan이 "기존 구조 우선"을 지시하므로 `git.toki-labs.com/toki/common-proto-socket/go => ../proto-socket/go` replace를 유지. reproducibility는 proto-socket repo가 clean(SHA 고정)이라는 별도 증거로 보장한다.
- **iop.db 복구 정책**: 본 task는 transport tests의 iop.db 부수효과 root cause를 다루지 않는다. 검증 계약 만족만을 위해 검증 사이에 명시적으로 `git checkout HEAD -- iop.db`로 상태를 복구하고, 최종 mock/Gemini e2e의 before==after stat 동일성과 최종 `git status --short -- iop.db` empty로 acceptance를 닫는다.

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

- `/config/workspace/proto-socket` working tree가 clean/versioned 상태인가. → HEAD=`c9d21b9`, `status --short` empty (검증 결과 참조).
- go-iop 검증이 uncommitted proto-socket changes에 의존하지 않는가. → proto-socket 측 commit 후 모든 go-iop test/e2e/Gemini idle을 재실행해 PASS 확인.
- proto-socket heartbeat tests가 stale wait timer와 legitimate timeout을 모두 검증하는가. → `TestHeartbeatSurvivesPairedIdle`(stale wait-timer race), `TestHeartbeatDisconnectsWithoutResponse`/`TestWsHeartbeatDisconnectsWithoutResponse`(legitimate timeout + DisconnectReason 검증) — 모두 PASS (`go test ./test` 8.066s).
- Gemini 600s idle 결과 또는 외부 BLOCKED reason이 실제 로그로 기록됐는가. → `[e2e] Real profile (gemini) status: PASSED`, `[e2e] Smoke test PASSED.` — `/tmp/iop-protodep-logs/gemini-idle-600.log`.

## 검증 결과

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.

### PROTODEP-1 중간 검증
```bash
$ git -C /config/workspace/proto-socket diff --check
(no output; exit 0)
$ (cd /config/workspace/proto-socket/go && go test -count=1 ./...)
ok  	git.toki-labs.com/toki/common-proto-socket/go	0.006s
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/dart_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/kotlin_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/python_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/typescript_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/examples/tcp_echo	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/examples/ws_echo	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/packets	[no test files]
ok  	git.toki-labs.com/toki/common-proto-socket/go/test	8.123s
(exit 0)
$ git -C /config/workspace/proto-socket status --short
(no output; exit 0)
```

### PROTODEP-2 중간 검증
```bash
$ git -C /config/workspace/proto-socket rev-parse HEAD
c9d21b988805857c260ebeee4c708f82f1958212
(exit 0)
$ git -C /config/workspace/proto-socket status --short
(no output; exit 0)
$ git status --short -- iop.db
 D iop.db
(exit 0)
# 위 ` D iop.db`는 직전 단계의 `go test ./apps/node/internal/transport` 부수효과.
# 사전 존재 이슈로 본 task 범위 외. `git checkout HEAD -- iop.db`로 복구 후 다음 검증으로 진행.
# (`계획 대비 변경 사항` 참조)
$ go test -count=1 ./apps/node/internal/transport ./apps/edge/internal/transport
ok  	iop/apps/node/internal/transport	5.168s
ok  	iop/apps/edge/internal/transport	0.006s
(exit 0)
```

### PROTODEP-3 중간 검증
```bash
$ mkdir -p /tmp/iop-protodep-logs
(no output; exit 0)
$ before=$(stat -c '%Y %s' iop.db 2>/dev/null || echo missing)
before=1779007121 28672
$ IOP_E2E_IDLE_SECONDS=2 make test-e2e > /tmp/iop-protodep-logs/mock-idle-2.log 2>&1
(exit 0)
$ after=$(stat -c '%Y %s' iop.db 2>/dev/null || echo missing)
after=1779007121 28672
$ test "$before" = "$after"
(exit 0)
$ grep -E "Smoke test|node-test-node-(capabilities|transport|sessions)|idle monitor" /tmp/iop-protodep-logs/mock-idle-2.log
[e2e] idle monitor: sleeping 2s (edge baseline=39 node baseline=33)
edge> [node-test-node-capabilities] target=fake-cat session=default
edge> [node-test-node-transport] target=fake-cat session=default
edge> [node-test-node-sessions] target=fake-cat session=session2
[e2e] Smoke test PASSED.
(exit 0)
```

### 최종 검증
```bash
$ git -C /config/workspace/proto-socket diff --check
(no output; exit 0)
$ (cd /config/workspace/proto-socket/go && go test -count=1 ./...)
ok  	git.toki-labs.com/toki/common-proto-socket/go	0.005s
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/dart_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/kotlin_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/python_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/crosstest/typescript_go_client	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/examples/tcp_echo	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/examples/ws_echo	[no test files]
?   	git.toki-labs.com/toki/common-proto-socket/go/packets	[no test files]
ok  	git.toki-labs.com/toki/common-proto-socket/go/test	8.066s
(exit 0)
$ git -C /config/workspace/proto-socket status --short
(no output; exit 0)
$ git -C /config/workspace/proto-socket rev-parse HEAD
c9d21b988805857c260ebeee4c708f82f1958212
(exit 0)
$ go test -count=1 ./...
?   	iop/apps/control-plane/cmd/control-plane	[no test files]
?   	iop/apps/edge/cmd/edge	[no test files]
ok  	iop/apps/edge/internal/bootstrap	0.010s
ok  	iop/apps/edge/internal/events	0.004s
ok  	iop/apps/edge/internal/node	0.010s
ok  	iop/apps/edge/internal/opsconsole	0.007s
ok  	iop/apps/edge/internal/service	0.007s
ok  	iop/apps/edge/internal/transport	0.010s
?   	iop/apps/node/cmd/node	[no test files]
ok  	iop/apps/node/internal/adapters	0.008s
ok  	iop/apps/node/internal/adapters/cli	39.077s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	31.002s
?   	iop/apps/node/internal/adapters/mock	[no test files]
?   	iop/apps/node/internal/adapters/ollama	[no test files]
?   	iop/apps/node/internal/adapters/vllm	[no test files]
ok  	iop/apps/node/internal/bootstrap	0.169s
ok  	iop/apps/node/internal/node	0.015s
ok  	iop/apps/node/internal/router	0.010s
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	0.040s
ok  	iop/apps/node/internal/transport	5.084s
?   	iop/apps/worker/cmd/worker	[no test files]
?   	iop/packages/auth	[no test files]
ok  	iop/packages/config	0.010s
?   	iop/packages/events	[no test files]
?   	iop/packages/jobs	[no test files]
?   	iop/packages/metadata	[no test files]
?   	iop/packages/observability	[no test files]
?   	iop/packages/policy	[no test files]
?   	iop/packages/version	[no test files]
?   	iop/proto/gen/iop	[no test files]
(exit 0)
# 위 `go test ./...` 실행으로 root iop.db가 다시 deleted 상태가 됨.
# 다음 mock e2e의 before/after 비교를 plan 계약대로 수행하기 위해
# `git checkout HEAD -- iop.db`로 복구 후 진행 (`계획 대비 변경 사항` 참조).
$ mkdir -p /tmp/iop-protodep-logs
(no output; exit 0)
$ before=$(stat -c '%Y %s' iop.db 2>/dev/null || echo missing)
before=1779007121 28672
$ IOP_E2E_IDLE_SECONDS=2 make test-e2e > /tmp/iop-protodep-logs/mock-idle-2.log 2>&1
(exit 0)
$ after=$(stat -c '%Y %s' iop.db 2>/dev/null || echo missing)
after=1779007121 28672
$ test "$before" = "$after"
(exit 0)
$ grep -E "Smoke test|node-test-node-(capabilities|transport|sessions)|idle monitor" /tmp/iop-protodep-logs/mock-idle-2.log
[e2e] idle monitor: sleeping 2s (edge baseline=39 node baseline=33)
edge> [node-test-node-capabilities] target=fake-cat session=default
edge> [node-test-node-transport] target=fake-cat session=default
edge> [node-test-node-sessions] target=fake-cat session=session2
[e2e] Smoke test PASSED.
(exit 0)
$ IOP_E2E_PROFILE=gemini IOP_E2E_IDLE_SECONDS=600 make test-e2e > /tmp/iop-protodep-logs/gemini-idle-600.log 2>&1
(exit 0)
$ grep -E "idle monitor|idle window|Real profile|Smoke test|heartbeat|transport_close|disconnected from edge" /tmp/iop-protodep-logs/gemini-idle-600.log
[e2e] idle monitor: sleeping 600s (edge baseline=39 node baseline=32)
{"level":"info","ts":1779007928.467462,"caller":"transport/server.go:175","msg":"node unregistered","node_id":"test-node","transport_close_reason":"local_close","transport_close_error":"read tcp 127.0.0.1:31056->127.0.0.1:40416: use of closed network connection"}
{"level":"info","ts":1779007928.467525,"caller":"transport/session.go:89","msg":"disconnected from edge","transport_close_reason":"remote_closed","transport_close_error":"EOF"}
[edge-event] disconnected reason="transport_closed" transport_close_reason="remote_closed" transport_close_error="EOF"
[e2e] Real profile (gemini) status: PASSED
[e2e] Smoke test PASSED.
(exit 0)
$ git status --short -- iop.db
(no output; exit 0)
$ git diff --check
(no output; exit 0)
```

---

> **[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 the review-agent-only checklist unchanged.

## 코드리뷰 결과

### Review 0 - 2026-05-17

- 종합 판정: PASS
- 차원별 평가:
  - correctness: Pass
  - completeness: Pass
  - test coverage: Pass
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Pass
- 발견된 문제: 없음
- 검토 중 재실행:
  - `(cd /config/workspace/proto-socket/go && go test -count=1 ./...)` PASS (`go/test` 8.077s)
  - `go test -count=1 ./...` PASS in temporary sibling worktree `/config/workspace/go-iop-review-codex-3030`
  - `/tmp/iop-protodep-logs/mock-idle-2.log` and `/tmp/iop-protodep-logs/gemini-idle-600.log` grep evidence matched the recorded smoke/Gemini acceptance output.
- 다음 단계: PASS - active plan/review 파일을 archive log로 이동하고 `complete.log` 작성 후 종료.
