<!-- task=m-control-plane-separation-migration/04+02_bootstrap_command plan=3 tag=USER_DECISION_IOSMOKE -->

# Plan - USER_DECISION_IOSMOKE

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

이 plan은 USER_REVIEW 후속 루프다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션만 채우고 active 파일을 그대로 둔다. 사용자에게 직접 질문하거나 선택지를 제시하지 않는다.

## 배경

사용자 결정으로 legacy iop smoke는 별도 iop 쪽 OTO 제거 작업의 검증 대상이며, 이 OTO 독립 마이그레이션 완료 기준에서 제외한다. 이번 루프는 그 결정을 local agent-smoke 계약과 최종 검증 명령에 반영하고, 남은 analyzer info를 정리한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 질문, `USER_REVIEW.md`, archive, `complete.log` 작성은 금지된다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- Task ids:
  - `bootstrap-command`: OTO Server가 runner bootstrap command를 발급하고, bootstrap script가 `apps/runner` binary/config를 기준으로 runner를 실행하는 흐름이 정의된다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/agent-smoke.md`
- `apps/runner/test/oto_server_connection_smoke_test.dart`
- `apps/runner/test/oto_agent_migration_plan_test.dart`
- `apps/runner/test/oto_iop_connection_smoke_test.dart`
- `agent-task/m-control-plane-separation-migration/04+02_bootstrap_command/USER_REVIEW.md`
- `agent-task/m-control-plane-separation-migration/04+02_bootstrap_command/code_review_cloud_G07_2.log`

### 테스트 환경 규칙

- test_env: local.
- `agent-test/local/rules.md`와 `agent-test/local/agent-smoke.md`를 읽었다.
- 사용자 결정에 따라 `agent-smoke`의 필수 unit 명령에서 `test/oto_iop_connection_smoke_test.dart`를 제외했다.
- 적용 최종 명령:
  - `cd services/core && go test ./...`
  - `cd apps/runner && dart analyze`
  - `cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_bootstrap_script_test.dart test/oto_agent_cli_test.dart test/oto_agent_config_test.dart test/oto_agent_registration_test.dart test/oto_server_connection_smoke_test.dart`

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

- OTO Server registration/online smoke: `test/oto_server_connection_smoke_test.dart`가 검증한다.
- server-issued bootstrap command script success path: `test/oto_server_connection_smoke_test.dart`가 fake `curl`/archive로 검증한다.
- legacy iop smoke: 이 task의 필수 검증에서 제외한다. 별도 iop 제거 작업의 검증 대상이다.

### 심볼 참조

- 제거/변경 대상 symbol 없음.
- 검증 명령에서 `test/oto_iop_connection_smoke_test.dart`만 제외한다.

### 분할 판단

- 단일 plan으로 진행한다.
- 변경은 test rule 계약 정리와 analyzer cleanup에 한정된다.
- API/foundation과 call-site rollout 분리, 다중 도메인 구현 분리, 독립 위험 경계가 없다.

### 범위 결정 근거

- sibling `iop` 저장소와 `apps/runner/test/oto_iop_connection_smoke_test.dart` 자체 수정은 제외한다.
- OTO Server bootstrap command 구현은 유지하고, analyzer cleanup과 검증 계약 정리만 수행한다.

### 빌드 등급

- cloud-G07: shell/bootstrap smoke와 최종 검증 계약 회복이 중심이고 이전 루프에서 verification trust failure가 반복되었다.

## 구현 체크리스트

- [ ] [USER_DECISION_IOSMOKE-1] agent-smoke local 테스트 계약에서 legacy iop smoke를 필수 검증에서 제외하고, 라우팅 설명도 OTO Server 기준으로 정리한다.
- [ ] [USER_DECISION_IOSMOKE-2] `apps/runner/test/oto_server_connection_smoke_test.dart`의 analyzer info 2건을 제거하고, smoke command 조립을 읽기 쉬운 형태로 정리한다.
- [ ] [USER_DECISION_IOSMOKE-3] iop smoke를 제외한 최종 검증 명령을 실행하고 실제 stdout/stderr를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [USER_DECISION_IOSMOKE-1] agent-smoke 계약 정리

문제: `agent-test/local/agent-smoke.md`의 unit 명령이 legacy `test/oto_iop_connection_smoke_test.dart`를 포함한다. 사용자는 iop가 별도 OTO 제거 작업 중이며 이 task에서 고려할 항목이 아니라고 결정했다.

해결 방법:

- `agent-test/local/agent-smoke.md` unit 명령에서 `test/oto_iop_connection_smoke_test.dart`를 제거한다.
- `agent-test/local/rules.md` 라우팅 설명에서 iop connection smoke 문구를 제거한다.
- local update-test 규약에 따라 `.gitignore`에 `agent-test/local/`, `agent-test/runs/`가 있는지 확인한다.

수정 파일 및 체크리스트:

- [ ] `agent-test/local/rules.md`
- [ ] `agent-test/local/agent-smoke.md`
- [ ] `.gitignore`

테스트 작성: 문서/검증 계약 변경이므로 별도 test file은 추가하지 않는다.

중간 검증:

```bash
rg --sort path "oto_iop_connection_smoke_test.dart" agent-test/local
```

기대 결과: 출력 없음.

### [USER_DECISION_IOSMOKE-2] analyzer cleanup

문제: `apps/runner/test/oto_server_connection_smoke_test.dart:203`의 `\#` escape와 `:228`의 문자열 `+` 조립 때문에 `dart analyze`가 info 2건을 출력한다.

해결 방법:

- fake curl script 문자열에서 `$#` 표현은 `\$#`로 남기고 불필요한 `\#` escape를 제거한다.
- `fullCommand`는 list join 또는 interpolation으로 구성해 `prefer_interpolation_to_compose_strings`를 제거한다.

수정 파일 및 체크리스트:

- [ ] `apps/runner/test/oto_server_connection_smoke_test.dart`

테스트 작성: 기존 smoke test를 유지한다.

중간 검증:

```bash
cd apps/runner && dart analyze
```

기대 결과: `No issues found!`

### [USER_DECISION_IOSMOKE-3] 최종 검증

문제: 이전 최종 검증 명령이 legacy iop smoke를 포함해 sibling repo compile error로 실패했다.

해결 방법:

- 사용자 결정에 맞춰 final command에서 `test/oto_iop_connection_smoke_test.dart`를 제외한다.
- OTO Server migration/source evidence test는 `test/oto_agent_migration_plan_test.dart`로 유지한다.

수정 파일 및 체크리스트:

- [ ] `agent-task/m-control-plane-separation-migration/04+02_bootstrap_command/CODE_REVIEW-cloud-G07.md`

테스트 작성: 없음. 최종 검증 command 계약 정리다.

중간 검증:

```bash
cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_bootstrap_script_test.dart test/oto_agent_cli_test.dart test/oto_agent_config_test.dart test/oto_agent_registration_test.dart test/oto_server_connection_smoke_test.dart
```

기대 결과: `All tests passed!`

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `agent-test/local/rules.md` | USER_DECISION_IOSMOKE-1 |
| `agent-test/local/agent-smoke.md` | USER_DECISION_IOSMOKE-1 |
| `.gitignore` | USER_DECISION_IOSMOKE-1 |
| `apps/runner/test/oto_server_connection_smoke_test.dart` | USER_DECISION_IOSMOKE-2 |
| `agent-task/m-control-plane-separation-migration/04+02_bootstrap_command/CODE_REVIEW-cloud-G07.md` | USER_DECISION_IOSMOKE-3 |

## 최종 검증

```bash
cd services/core && go test ./...
cd apps/runner && dart analyze
cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_bootstrap_script_test.dart test/oto_agent_cli_test.dart test/oto_agent_config_test.dart test/oto_agent_registration_test.dart test/oto_server_connection_smoke_test.dart
```

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