<!-- task=m-oto-registration-online-smoke/03+01,02_smoke_evidence plan=0 tag=TEST -->

# OTO Registration Online Smoke Evidence

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

이 plan은 실제 smoke/evidence 수집을 끝내는 작업입니다. 구현 후 active `CODE_REVIEW-*-G??.md`의 구현 소유 섹션에 실행 명령과 실제 stdout/stderr를 남기고, code-review가 PASS/complete/archive를 처리하게 두세요.

## 배경

iop Edge lifecycle foundation과 OTO persistent session이 완료되어도 Milestone은 실제 same-host smoke evidence가 남아야 완료될 수 있다. 현재 OTO smoke는 accepted response만 확인하고, iop repo에는 OTO smoke를 재현하는 공식 Makefile/script entrypoint가 없다. 이 subtask는 same-host 기본 smoke를 재현 가능한 명령으로 묶고, heartbeat online과 unlock evidence를 남긴다.

## 사용자 리뷰 요청 흐름

사용자-owned 외부 host, credential, private endpoint가 꼭 필요해지면 review stub의 `사용자 리뷰 요청`에 근거를 남긴다. local same-host smoke에서 해결 가능한 실패는 사용자 리뷰 요청이 아니라 후속 구현/검증 이슈다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-registration-online-smoke.md`
- Task ids:
  - `heartbeat-online`: 최초 heartbeat 도착 후 online으로 전환된다. 검증: OTO Dart smoke 또는 통합 smoke에서 registration accepted 뒤 heartbeat 도착까지 확인한다.
  - `unlock-evidence`: OTO `oto-agent` 등록 흐름 잠금 해제에 사용할 smoke 결과와 실행 명령이 문서 또는 test log에 남는다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-registration-online-smoke.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/testing-smoke.md`
- `Makefile`
- `../oto/AGENTS.md`
- `../oto/agent-ops/rules/project/rules.md`
- `../oto/agent-ops/rules/project/domain/agent/rules.md`
- `../oto/agent-ops/rules/project/domain/framework/rules.md`
- `../oto/pubspec.yaml`
- `../oto/lib/oto/agent/edge_registration_client.dart`
- `../oto/lib/oto/agent/agent_runner.dart`
- `../oto/test/oto_iop_connection_smoke_test.dart`
- `scripts/e2e-smoke.sh` 일부: 기존 generic node smoke 패턴 확인용. 이 plan에서는 직접 수정하지 않는다.

### 테스트 환경 규칙

- iop test_env: `local`, profiles `edge-smoke`, `testing-smoke`.
- iop required commands: target Go tests, changed user entrypoint full-cycle, optional `make test-e2e`.
- OTO `agent-test/local`은 없음. OTO domain fallback: `dart analyze`, agent tests, `test/oto_iop_connection_smoke_test.dart`.
- preflight: `command -v dart` 성공, but current OTO dependency solving failed before subtask 02.

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

- OTO smoke does not assert heartbeat or Edge online event.
- iop Makefile lacks an OTO-specific smoke target.
- Current OTO smoke hardcodes `workingDirectory: '../iop'`, which is fragile for this workspace where current repo is `iop2oto`.
- No tracked evidence file/log command exists for OTO unlock handoff.

### 심볼 참조

- 변경/추가 후보: Makefile `.PHONY` and test targets; no symbol rename expected.
- OTO smoke references `EdgeRegistrationClient.register()` at `../oto/test/oto_iop_connection_smoke_test.dart:64`.

### 분할 판단

- This subtask is `03+01,02_smoke_evidence`.
- Required predecessors:
  - `01_iop_edge_lifecycle`: no active/archive `complete.log` yet.
  - `02+01_oto_agent_session`: no active/archive `complete.log` yet.
- The script/evidence work is split because it has cross-process verification and should not start until both code foundations are reviewed.

### 범위 결정 근거

- Do not merge OTO smoke into generic `scripts/e2e-smoke.sh`; that script is generic Edge-Node smoke and already large.
- Do not implement build/deploy run payload, artifact stream, or Control Plane UI.
- Do not commit secret/token values; smoke uses generated temp dirs and non-secret test token.
- Split-host smoke is out of default path; support only documented env overrides if cheap.

### 빌드 등급

- build: `cloud-G07`, review: `cloud-G07`. This is shell/CLI workflow and cross-repo process orchestration with evidence trust risk.

## 의존 관계 및 구현 순서

이 subtask는 디렉터리명 `03+01,02_smoke_evidence`에 따라 아래 predecessor complete logs가 필요하다.

- `01_iop_edge_lifecycle`: `agent-task/m-oto-registration-online-smoke/01_*/complete.log` 또는 archive equivalent.
- `02+01_oto_agent_session`: `agent-task/m-oto-registration-online-smoke/02+01_*/complete.log` 또는 archive equivalent.

구현 시작 전 둘 다 정확히 하나씩 확인한다. 없거나 ambiguous하면 구현하지 말고 review stub에 blocker를 기록한다.

## 구현 체크리스트

- [ ] OTO smoke가 iop repo path를 env로 받을 수 있게 하거나 iop-side wrapper가 현재 repo를 명시하게 한다.
- [ ] OTO Dart smoke 또는 iop wrapper가 registration accepted 뒤 heartbeat/online event를 확인한다.
- [ ] `make test-oto-registration-online-smoke` 또는 동등한 iop Makefile target을 추가한다.
- [ ] smoke output에 unlock handoff용 실행 명령과 accepted/heartbeat/online evidence가 남게 한다.
- [ ] `go test -count=1 ./apps/edge/internal/transport ./apps/edge/internal/node ./apps/edge/internal/service ./packages/go/events`와 OTO smoke target을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [TEST-1] Make OTO Smoke Repo Path Explicit

문제: [oto_iop_connection_smoke_test.dart](/config/workspace/oto/test/oto_iop_connection_smoke_test.dart:31)는 `workingDirectory: '../iop'`를 하드코딩한다. `iop2oto` clone에서는 실패한다.

해결 방법: OTO test에 `IOP_REPO_ROOT` env override를 추가하거나, iop-side wrapper에서 OTO test를 실행할 때 cwd/env를 명시한다. 기존 same-host 기본은 유지한다.

수정 파일 및 체크리스트:

- [ ] `../oto/test/oto_iop_connection_smoke_test.dart` env override 추가 또는 wrapper-only 전략 선택.
- [ ] `../oto/test/oto_iop_connection_smoke_test.dart` temp config에 `agent_kind: "oto-agent"` 포함.
- [ ] hardcoded `../iop`가 남지 않는지 `rg --sort path "../iop|IOP_REPO_ROOT" ../oto/test/oto_iop_connection_smoke_test.dart`로 확인.

테스트 작성: 기존 smoke test를 수정해 path override가 적용되는지 same test에서 사용한다.

중간 검증:

```bash
rg --sort path "../iop|IOP_REPO_ROOT|agent_kind" ../oto/test/oto_iop_connection_smoke_test.dart
```

예상 결과: `IOP_REPO_ROOT`와 `agent_kind`가 보이고 hardcoded-only path가 없다.

### [TEST-2] Assert Accepted Then Heartbeat Online

문제: [oto_iop_connection_smoke_test.dart](/config/workspace/oto/test/oto_iop_connection_smoke_test.dart:67)는 `result.accepted`만 확인한다. online event나 heartbeat evidence가 없다.

해결 방법: OTO persistent session API로 registration 후 연결을 유지하고, iop Edge log/event output 또는 service-visible state를 확인한다. 가장 작은 경로는 Edge stdout/console log에 `node.online` or lifecycle online marker를 기다리는 것이다.

수정 파일 및 체크리스트:

- [ ] `../oto/test/oto_iop_connection_smoke_test.dart` accepted 뒤 online marker wait 추가.
- [ ] timeout failure message에 Edge output을 포함.
- [ ] test finally에서 persistent session과 Edge process를 닫는다.

테스트 작성: existing smoke test를 heartbeat online까지 확장한다.

중간 검증:

```bash
cd ../oto && IOP_REPO_ROOT=/config/workspace/iop2oto dart test test/oto_iop_connection_smoke_test.dart
```

예상 결과: exit code 0, output contains accepted and online evidence.

### [TEST-3] iop Makefile Target and Evidence Command

문제: [Makefile](/config/workspace/iop2oto/Makefile:77)는 generic `test-e2e`만 있고 OTO online smoke target이 없다.

해결 방법: `.PHONY`에 `test-oto-registration-online-smoke`를 추가하고 target은 `cd ../oto && IOP_REPO_ROOT=$(CURDIR) dart test test/oto_iop_connection_smoke_test.dart`를 실행한다. 필요하면 `scripts/e2e-oto-registration-online-smoke.sh`를 새로 만들되, generic `scripts/e2e-smoke.sh`에 섞지 않는다.

수정 파일 및 체크리스트:

- [ ] `Makefile` `.PHONY`에 target 추가.
- [ ] `Makefile` target 추가 또는 새 `scripts/e2e-oto-registration-online-smoke.sh` 추가.
- [ ] output에 unlock handoff용 command line을 echo한다.

테스트 작성: Makefile target 자체가 smoke test다. 별도 unit test는 skip.

중간 검증:

```bash
make test-oto-registration-online-smoke
```

예상 결과: exit code 0.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `../oto/test/oto_iop_connection_smoke_test.dart` | TEST-1, TEST-2 |
| `Makefile` | TEST-3 |
| `scripts/e2e-oto-registration-online-smoke.sh` | TEST-3, only if wrapper script is cleaner than inline Makefile command |

## 최종 검증

```bash
go test -count=1 ./apps/edge/internal/transport ./apps/edge/internal/node ./apps/edge/internal/service ./packages/go/events
cd ../oto && dart analyze
make test-oto-registration-online-smoke
```

예상 결과: 모두 exit code 0. Smoke output must include the command used, registration accepted evidence, and first-heartbeat/online evidence. Go test cache output은 허용하지 않는다.

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