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

# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Fill implementation-owned sections, paste verification output, and stop with active files in place.

## 개요

date=2026-06-05
task=m-control-plane-separation-migration/04+02_bootstrap_command, plan=0, tag=API

## 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

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

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] Predecessor 확인 | [x] |
| [API-2] Server bootstrap command endpoint | [x] |
| [API-3] Bootstrap script server terminology | [x] |
| [API-4] Config parser와 CLI help | [x] |
| [API-5] Full bootstrap smoke | [x] |

## 구현 체크리스트

- [x] [API-1] predecessor `02+01_runner_registration` 완료 근거를 확인한다.
- [x] [API-2] services/core에 runner bootstrap command response를 추가한다.
- [x] [API-3] bootstrap script가 `--server-url` 표준 flag와 `server:` config section을 쓰도록 갱신하되 `--edge-url` compatibility를 유지하거나 명시적으로 deprecate한다.
- [x] [API-4] Dart config parser와 CLI help가 OTO Server 용어를 우선 사용한다.
- [x] [API-5] Go/Dart script tests를 추가하고 token 노출 방지와 PID path 회귀를 유지한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.

- [x] 판정을 append한다.
- [x] active plan/review를 log로 아카이브한다.
- [ ] PASS이면 complete.log 작성 후 task directory를 archive로 이동한다.
- [ ] PASS이고 m-prefixed task group이면 완료 이벤트 메타데이터만 보고한다.
- [x] WARN/FAIL이면 다음 loop 상태를 만든다.

## 계획 대비 변경 사항

없음.

## 주요 설계 결정

1. `BootstrapCommandRequest`에 `enrollment_token` 필드를 추가하여, 서버가 bootstrap command 생성 시 올바른 enrollment token을 포함하여 curl/bash 명령을 구성하도록 설계함.
2. Go HTTP Server에 `/bootstrap/oto-agent.sh` 엔드포인트를 추가하여 실제 bootstrap script의 내용을 동적으로 읽고 서빙하도록 구현함. 테스트 시 다양한 작업 디렉토리 경로에 대응할 수 있도록 상대 경로 검색 로직을 적용함.
3. bootstrap script가 `--edge-url`을 `--server-url`의 호환성 alias로 취급하며 생성되는 yaml 설정 파일 역시 `server:` 섹션을 우선하도록 변경함.

## 사용자 리뷰 요청

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

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

- server-issued bootstrap command와 shell flag 이름이 일치한다.
- `--server-url`이 표준이고 `--edge-url` 처리 방침이 테스트로 고정된다.
- enrollment token이 stdout/stderr에 노출되지 않는다.
- PID path 회귀가 유지된다.

## 검증 결과

### API-1 중간 검증
```bash
$ find agent-task/archive/2026/06/m-control-plane-separation-migration/02+01_runner_registration -name complete.log -print
agent-task/archive/2026/06/m-control-plane-separation-migration/02+01_runner_registration/complete.log
```

### API-2 중간 검증
```bash
$ cd services/core && go test ./internal/httpserver
ok      github.com/toki/oto/services/core/internal/httpserver   0.005s
```

### API-3 중간 검증
```bash
$ cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart
00:00 +0: loading test/oto_agent_bootstrap_script_test.dart
00:00 +0: Linux Bootstrap Script Contract Tests (setUpAll)
00:00 +0: Linux Bootstrap Script Contract Tests should contain required flags
00:00 +1: Linux Bootstrap Script Contract Tests should contain required flags
00:00 +2: Linux ... Contract Tests should support correct architecture assets
00:00 +3: Linux ... Tests should write expected keys to configuration yaml
00:00 +4: Linux ... Tests should write expected keys to configuration yaml
00:00 +4: Linux ... contain agent runtime commands and background behavior
00:00 +5: Linux Bootstrap Script Contract Tests (tearDownAll)
00:00 +5: Linux ... Tests should fail when release base URL is not HTTPS
00:00 +6: Linux ... Tests should fail when release base URL is not HTTPS
00:00 +6: Linux ... agent state path when custom config-path is specified
00:00 +7: Linux ... agent state path when custom config-path is specified
00:00 +7: Linux ... Tests should support --edge-url as alias compatibility
00:00 +8: Linux ... Tests should support --edge-url as alias compatibility
00:08 +8: All tests passed!
```

### API-4 중간 검증
```bash
$ cd apps/runner && dart test test/oto_agent_config_test.dart test/oto_agent_cli_test.dart
00:00 +0: loading test/oto_agent_config_test.dart
00:00 +0: loading test/oto_agent_cli_test.dart
00:00 +1: AgentConfig parses bootstrap config yaml
00:00 +2: AgentConfig parses server section as standard connection config
00:00 +3: AgentConfig preserves empty alias
00:00 +4: AgentConfig parses missing alias as null
00:00 +5: AgentConfig rejects missing required values
00:00 +6: AgentConfig rejects invalid field types
00:00 +7: AgentConfig fromFile reads file correctly
00:00 +8: AgentConfig fromFile throws when file not found
00:00 +9: CommandAgent parses run config and calls runner
00:00 +10: CommandAgent parses server config and calls runner
00:00 +11: CommandAgent parses run config with equals option and calls runner
00:00 +12: CommandAgent rejects unknown subcommand
00:00 +13: CommandAgent rejects missing config parameter
00:00 +14: CommandAgent sets exit code zero on accepted registration
00:00 +15: CommandAgent sets exit code ten on rejected registration
00:01 +15: actual bin agent help lists run usage
00:02 +15: actual bin agent help lists run usage
00:03 +15: actual bin agent help lists run usage
00:03 +16: actual bin agent help lists run usage
00:03 +16: actual bin agent run help works
00:04 +16: actual bin agent run help works
00:05 +16: actual bin agent run help works
00:06 +16: actual bin agent run help works
00:06 +17: actual bin agent run help works
00:06 +17: actual bin agent with no parameters shows root help
00:07 +17: actual bin agent with no parameters shows root help
00:08 +17: actual bin agent with no parameters shows root help
00:09 +17: actual bin agent with no parameters shows root help
00:09 +18: actual bin agent with no parameters shows root help
00:09 +18: All tests passed!
```

### API-5 중간 검증
```bash
$ cd services/core && go test ./internal/httpserver
ok      github.com/toki/oto/services/core/internal/httpserver   0.005s

$ cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart
00:08 +8: All tests passed!
```

### 최종 검증
```bash
$ cd services/core && go test ./...
?       github.com/toki/oto/services/core/cmd/oto-core  [no test files]
ok      github.com/toki/oto/services/core/internal/httpserver   0.005s
ok      github.com/toki/oto/services/core/internal/runnerregistry       0.003s
?       github.com/toki/oto/services/core/oto   [no test files]

$ cd apps/runner && dart analyze
Analyzing runner...
No issues found!

$ 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
00:00 +0: loading test/oto_agent_migration_plan_test.dart
00:00 +1: ...
00:02 +41: All tests passed!
```

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail
  - Completeness: Fail
  - Test coverage: Fail
  - API contract: Fail
  - Code quality: Pass
  - Plan deviation: Warn
  - Verification trust: Fail
- 발견된 문제:
  - Required: `services/core/internal/httpserver/server.go:293`에서 server-issued command가 `serverURL`을 `--release-base-url`에도 그대로 넣습니다. OTO Core의 기본/테스트 서버는 HTTP로 뜨는데, bootstrap script는 `apps/runner/assets/script/shell/oto_agent_bootstrap.sh:82`-`86`에서 `--release-base-url`이 `https://`가 아니면 즉시 실패합니다. 실제 재현: `bash apps/runner/assets/script/shell/oto_agent_bootstrap.sh --server-url http://127.0.0.1:18080 --agent-id test-runner --enrollment-token test-token --release-base-url http://127.0.0.1:18080/releases --no-background` -> `Error: --release-base-url must use https://`. 서버가 발급한 command를 script 입력으로 실행 가능한 계약으로 고치고, fake curl/tar fixture로 endpoint command를 실제 script에 통과시키는 smoke를 추가하세요.
  - Required: `services/core/internal/httpserver/server.go:293`에서 `r.Host`, `runner_id`, `enrollment_token`을 shell command에 quote/escape 없이 직접 삽입합니다. Host header나 request body 값에 공백, quote, `;`, `$()` 같은 shell metacharacter가 들어가면 복사/실행용 bootstrap command가 인자 분리 또는 command injection 표면이 됩니다. command 인자는 shell-safe single-quote escaping으로 렌더링하거나 구조화된 response로 분리하고, Host/runner id/token 입력 검증 및 회귀 테스트를 추가하세요.
- 다음 단계: WARN/FAIL 후속 plan/review 파일을 작성한다.
