<!-- task=m-runtime-reconnect-config-refresh/01_reconnect_supervisor plan=1 tag=REVIEW_RECONNECT -->

# PLAN - REVIEW_RECONNECT Follow-up

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

이 계획은 `RECONNECT` 1차 구현의 코드리뷰 FAIL 항목만 해결한다. 범위를 넓혀 roadmap, SDD, agent-ops rule, field docs, refresh contract, node config apply를 수정하지 않는다.

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 구현의 마지막 단계다. 코딩 후 검증을 실행하고 실제 stdout/stderr를 붙인 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 스킬 전용이다.

선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 구현을 차단할 때만 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 남긴다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 검증 증거 공백, 환경/TTY 문제, 반복 실패는 사용자 리뷰 요청이 아니라 `검증 결과`, `계획 대비 변경 사항`, 또는 일반 follow-up으로 남긴다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/update-plane-self-update-foundation/milestones/runtime-reconnect-config-refresh.md`
- Task ids:
  - `retry-policy`: Node reconnect 기본값이 `interval=10s`, `max_attempts=10`으로 고정되고 config/문서/로그에 노출된다.
  - `supervisor`: Node bootstrap lifecycle이 최초 connect 성공 이후 Edge disconnect를 감지하고 reconnect supervisor로 세션을 재수립한다.
  - `exit-limit`: 재접속 실패가 retry 한계를 넘으면 Node가 adapter/store/session을 정리하고 process 종료 신호를 반환한다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Current archived plan: `agent-task/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/plan_cloud_G07_0.log`
- Current archived review: `agent-task/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/code_review_cloud_G07_0.log`
- Verdict: FAIL
- Required summary:
  - `apps/node/internal/bootstrap/module.go:203` starts reconnect attempt 1 immediately after remote disconnect; policy sleep only happens after failed attempts at `apps/node/internal/bootstrap/module.go:223`, which does not match the approved SDD state machine's reconnect_waiting interval.
  - `apps/node/internal/bootstrap/module_test.go:262` verifies `MaxAttempts=3`, `IntervalSec=0` without `WithSleeper`; SDD S01/S03 still need fake sleeper evidence for 10s interval and 10 attempts. `apps/node/cmd/node/main_test.go:77` does not assert reconnect fields in config print output.
  - `apps/node/internal/bootstrap/module_test.go:171` and `apps/node/internal/bootstrap/module_test.go:239` leave `WorkspaceRoot` empty, causing `storeDSN("")` to create untracked `apps/node/internal/bootstrap/iop.db` during package tests.
  - `agent-task/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/code_review_cloud_G07_0.log:200` recorded a TTY blocker instead of actual `scripts/dev/edge.sh` + `scripts/dev/node.sh` user-flow evidence. `./scripts/e2e-smoke.sh` itself reported that completion still requires that diagnostic.
- Reviewer verification:
  - `go test -count=1 ./packages/go/config ./apps/node/internal/transport ./apps/node/internal/bootstrap ./apps/edge/internal/transport` passed.
  - After the test, `apps/node/internal/bootstrap/iop.db` remained as an untracked SQLite file.
- Affected files expected:
  - `apps/node/internal/bootstrap/module.go`
  - `apps/node/internal/bootstrap/module_test.go`
  - `apps/node/cmd/node/main_test.go`
  - `apps/node/internal/bootstrap/iop.db` removal
  - `agent-task/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/CODE_REVIEW-cloud-G07.md`
- Roadmap carryover: keep the same Roadmap Targets. Do not include `dedupe` in PASS completion metadata.
- Archive files allowed for exact prior context: the two current archived files listed above only.

## 범위 결정 근거

- 포함: 리뷰 Required 항목을 닫기 위한 Node reconnect supervisor timing, retry policy evidence, config print assertion, test workspace isolation, required repo internal edge-node diagnostic evidence.
- 제외: Edge refresh entrypoint, proto/config refresh push contract, adapter diff/drain/runtime config apply, field docs/smoke, roadmap/SDD 문서 변경.
- `runtimeOwner.close()` error logging은 Suggested 항목이다. Required 해결 중 자연스럽게 처리할 수 있으면 반영하되, 이로 인해 범위를 넓히지 않는다.

## 구현 체크리스트

- [ ] SDD state machine에 맞게 remote disconnect 후 reconnect attempt가 `reconnect.interval_sec` policy를 통과하도록 supervisor timing을 정리한다. 검증: fake sleeper가 attempt 시작 전 또는 SDD 기준 interval 위치에서 `10s` duration을 관측한다.
- [ ] `MaxAttempts=10`, `IntervalSec=10` retry policy를 fake dialer/fake sleeper로 검증하고, Node `config print` 출력에 `reconnect.interval_sec`와 `reconnect.max_attempts`가 노출되는 assertion을 추가한다.
- [ ] bootstrap reconnect tests가 `t.TempDir()` 기반 `WorkspaceRoot`를 사용하게 고치고, 생성된 `apps/node/internal/bootstrap/iop.db` 산출물을 제거한다. 검증: 대상 테스트 후 `git status --short apps/node/internal/bootstrap/iop.db`가 비어 있다.
- [ ] PTY 또는 원격/code-server runner에서 `scripts/dev/edge.sh`와 `scripts/dev/node.sh` repo 내부 edge-node 진단을 수행해 `/nodes`, 같은 session 메시지 2회, node local payload와 edge rendered payload 동일성, `/capabilities`, `/transport`, `/sessions` 실제 출력을 `CODE_REVIEW-*-G??.md`에 기록한다.
- [ ] 최종 검증 명령과 repo 내부 edge-node 진단 결과를 `CODE_REVIEW-*-G??.md`에 실제 출력으로 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_RECONNECT-1] SDD Reconnect Interval Semantics

#### 문제

승인된 SDD는 `edge_disconnected` 뒤 `reconnect_waiting`을 거쳐 interval 경과 후 `reconnecting`으로 넘어가는 상태 흐름을 둔다. 현재 supervisor는 remote disconnect를 받으면 바로 attempt 1을 실행하고, sleep은 실패한 attempt 사이에만 적용한다.

#### 해결 방법

- `apps/node/internal/bootstrap/module.go`에서 remote disconnect 후 attempt 시작 전에 `cfg.Reconnect.IntervalSec` policy를 통과하도록 정리한다.
- `IntervalSec=0`은 테스트용 즉시 진행 seam으로 유지할 수 있다.
- 로그에는 attempt, max_attempts, interval_sec가 계속 남아야 한다.
- SDD 기준을 바꾸는 방식으로 해결하지 않는다.

#### 수정 파일 및 체크리스트

- [ ] `apps/node/internal/bootstrap/module.go`: reconnect attempt timing 수정
- [ ] `apps/node/internal/bootstrap/module_test.go`: fake sleeper로 interval 위치 검증

#### 테스트 작성

- 작성: remote disconnect 후 fake sleeper가 `10*time.Second`를 관측한 뒤 dial attempt가 시작되는 테스트.

#### 중간 검증

```bash
go test -count=1 ./apps/node/internal/bootstrap
```

### [REVIEW_RECONNECT-2] Retry Policy And Config Print Evidence

#### 문제

현재 exhaustion 테스트는 `MaxAttempts=3`, `IntervalSec=0`만 확인한다. SDD S01/S03와 PLAN은 fake sleeper/injectable seam으로 10초 정책과 10회 한계를 확인하라고 요구했다. Node config print 테스트도 YAML 출력이 비어 있지 않은지만 확인한다.

#### 해결 방법

- fake dialer와 fake sleeper를 함께 사용해 reconnect failure가 10회까지만 발생하는지 검증한다.
- fake sleeper가 받은 duration이 10초 policy와 일치하는지 검증한다.
- Node `config print` 테스트에서 출력 YAML에 `reconnect:`, `interval_sec: 10`, `max_attempts: 10`이 포함되는지 확인한다.

#### 수정 파일 및 체크리스트

- [ ] `apps/node/internal/bootstrap/module_test.go`: 10회 retry + 10초 sleeper assertion 추가
- [ ] `apps/node/cmd/node/main_test.go`: config print reconnect field assertion 추가

#### 테스트 작성

- 작성: `TestReconnectSupervisorExhaustsRetries` 또는 새 테스트에서 `MaxAttempts=10`, `IntervalSec=10`과 fake sleeper durations를 검증한다.
- 작성: `TestConfigPrintCmdPrintsResolvedConfig`에 reconnect field assertion을 추가한다.

#### 중간 검증

```bash
go test -count=1 ./apps/node/internal/bootstrap ./apps/node/cmd/node
```

### [REVIEW_RECONNECT-3] Bootstrap Test Workspace Isolation

#### 문제

새 bootstrap reconnect tests가 empty workspace root를 내려주어 SQLite store가 package cwd에 `iop.db`를 만든다. 이 산출물은 `.gitignore`에 잡히지 않고 리뷰어 재실행 후 untracked로 남았다.

#### 해결 방법

- successful bootstrap/reconnect test payload의 `NodeRuntimeConfig.WorkspaceRoot`를 `t.TempDir()`로 설정한다.
- 이미 생성된 `apps/node/internal/bootstrap/iop.db`를 제거한다.
- 테스트 재실행 뒤 해당 파일이 다시 생기지 않는지 확인한다.

#### 수정 파일 및 체크리스트

- [ ] `apps/node/internal/bootstrap/module_test.go`: reconnect/exhaustion mock payload workspace root를 temp dir로 설정
- [ ] `apps/node/internal/bootstrap/iop.db`: generated artifact 제거

#### 테스트 작성

- 기존 테스트를 보강한다. 별도 새 테스트는 불필요하다.

#### 중간 검증

```bash
go test -count=1 ./apps/node/internal/bootstrap
git status --short apps/node/internal/bootstrap/iop.db
```

기대 결과: Go test 통과, git status 출력 없음.

### [REVIEW_RECONNECT-4] Repo Internal Edge-Node Diagnostic Evidence

#### 문제

필수 repo 내부 edge-node 진단이 실제 실행 결과가 아니라 TTY blocker로 남아 있다. 보조 `./scripts/e2e-smoke.sh` 통과는 이 검증을 대체하지 않는다.

#### 해결 방법

- PTY가 있는 local shell, code-server, 또는 local rules의 원격 runner 중 하나를 사용한다.
- `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 별도 프로세스로 실행한다.
- edge console에서 `/nodes`, 같은 session 메시지 2회, `/capabilities`, `/transport`, `/sessions`를 실행한다.
- node local `[node-message]` payload와 edge `[node-*-message]` payload가 run별로 내용/순서까지 동일한지 기록한다.
- 실행 불가 시에는 어떤 runner/command가 왜 불가했는지 실제 출력과 함께 남긴다. 단, 이 경우 PASS completion은 기대하지 않는다.

#### 수정 파일 및 체크리스트

- [ ] `agent-task/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/CODE_REVIEW-cloud-G07.md`: 실제 진단 출력 기록

#### 테스트 작성

- 테스트 코드 추가는 불필요하다. 검증 evidence를 수집한다.

#### 중간 검증

```bash
./scripts/e2e-smoke.sh
scripts/dev/edge.sh
scripts/dev/node.sh
```

기대 결과: 보조 smoke 통과와 별도로 repo 내부 edge-node 진단의 메시지 2회 왕복 및 command 응답 evidence가 기록된다.

## 최종 검증

```bash
go test -count=1 ./packages/go/config ./apps/node/internal/transport ./apps/node/internal/bootstrap ./apps/edge/internal/transport
```

Expected: targeted config, node reconnect, bootstrap lifecycle, and edge transport tests pass.

```bash
go test -count=1 ./apps/node/... ./apps/edge/... ./packages/go/...
```

Expected: local node/edge/platform-common package regressions pass. Fresh execution is required.

```bash
./scripts/e2e-smoke.sh
```

Expected: auxiliary smoke passes and remains clearly separated from required repo internal diagnostic.

```bash
git status --short apps/node/internal/bootstrap/iop.db
```

Expected: no output.

Repo internal edge-node diagnostic remains required: run `scripts/dev/edge.sh` and `scripts/dev/node.sh` in separate processes and record `/nodes`, message x2, node-local/edge-rendered payload matching, `/capabilities`, `/transport`, and `/sessions`.
