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

# Plan - API

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 단계다. shell/bootstrap 검증 출력은 실제 stdout/stderr를 붙이고, active 파일을 그대로 둔 채 리뷰를 요청한다. 사용자 결정이나 외부 release artifact가 필요해 막히면 review stub의 `사용자 리뷰 요청` 섹션만 채운다.

## 배경

현재 bootstrap script는 `--edge-url`을 받아 `edge:` config를 생성한다. OTO Server가 runner bootstrap command를 발급하고, script가 `apps/runner` binary/config 기준으로 실행하려면 server 용어와 config shape를 등록 계약에 맞춰야 한다. shell entrypoint와 server-issued command가 맞물리므로 registration 작업 이후에 분리 실행한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 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

## 분석 결과

### 읽은 파일

- `apps/runner/assets/script/shell/oto_agent_bootstrap.sh`
- `apps/runner/test/oto_agent_bootstrap_script_test.dart`
- `apps/runner/lib/oto/agent/agent_config.dart`
- `apps/runner/lib/cli/commands/command_agent.dart`
- `services/core/internal/httpserver/server.go`
- `services/core/internal/httpserver/server_test.go`
- `agent-test/local/rules.md`
- `agent-test/local/agent-smoke.md`

### 테스트 환경 규칙

- `test_env=local`.
- agent profile 적용: script 변경 후 `cd apps/runner && dart analyze`, agent test subset.
- core service는 fallback `cd services/core && go test ./...`.
- `04+02`는 predecessor `02+01_runner_registration` 완료가 필요하다. 작성 시점에는 `02+01_runner_registration` active plan/review만 있고 `complete.log`가 없으므로, 구현 시작 전 완료 근거를 다시 확인해야 한다.

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

- Server-issued bootstrap command endpoint: 기존 Go tests 없음.
- Script `--server-url` compatibility: 기존 tests는 `--edge-url`만 본다.
- Config `server:` section generation: 기존 tests는 `edge:` keys만 본다.

### 심볼 참조

- `--edge-url`: `apps/runner/assets/script/shell/oto_agent_bootstrap.sh`, `apps/runner/test/oto_agent_bootstrap_script_test.dart`.
- `edge:` config: `apps/runner/lib/oto/agent/agent_config.dart`, `apps/runner/test/oto_agent_config_test.dart`, bootstrap script/test.

### 분할 판단

- shell/bootstrap orchestration은 registration model이 선행되어야 하므로 `04+02_bootstrap_command`로 분리한다.
- 작성 시점 predecessor 상태: missing complete log. `agent-task/m-control-plane-separation-migration/02+01_runner_registration/`에는 active plan/review만 있다.
- terminal/script 검증이 중심이라 별도 plan으로 둔다.

### 범위 결정 근거

- 실제 release artifact publishing, checksum signing, package hosting은 제외한다.
- background process manager나 service install 정책은 기존 script 수준만 유지한다.
- iop bootstrap command는 migration source로만 남기고 복제하지 않는다.

### 빌드 등급

- `cloud-G07`: shell/bootstrap command generation과 process execution 검증이 중심이라 cloud lane 고등급이 적합하다.

## 구현 체크리스트

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

## 의존 관계 및 구현 순서

- `04+02_bootstrap_command`는 `02+01_runner_registration`에 의존한다.
- 구현 전 `02_*` 또는 `02+*` matching `complete.log`를 확인한다.

### [API-1] Predecessor 확인

문제: bootstrap command에는 enrollment token과 server URL, runner id/alias config shape가 필요하다.

해결 방법: registration plan complete log를 확인한다.

수정 파일 및 체크리스트:

- [ ] active/archive predecessor complete log 확인

테스트 작성: 없음.

중간 검증:

```bash
find agent-task/m-control-plane-separation-migration -path '*/02*/*' -name complete.log -print
```

### [API-2] Server bootstrap command endpoint

문제: [services/core/internal/httpserver/server.go](/config/workspace/oto/services/core/internal/httpserver/server.go:19)는 bootstrap command endpoint가 없다.

해결 방법: `/api/v1/runners/bootstrap-command` 또는 plan 01 proto에 맞는 endpoint를 추가한다. response는 `curl -fsSL <server>/bootstrap/oto-agent.sh | bash -s -- --server-url ... --agent-id ... --enrollment-token ...` 형태의 command와 script URL을 포함한다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/httpserver/server.go`
- [ ] `services/core/internal/httpserver/server_test.go`
- [ ] 필요 시 `services/core/internal/runnerregistry/registry.go`

테스트 작성: Go handler tests. command contains server URL and token placeholder, rejects missing runner id/token.

중간 검증:

```bash
cd services/core && go test ./internal/httpserver
```

### [API-3] Bootstrap script server terminology

문제: [apps/runner/assets/script/shell/oto_agent_bootstrap.sh](/config/workspace/oto/apps/runner/assets/script/shell/oto_agent_bootstrap.sh:16)는 `--edge-url`, [apps/runner/assets/script/shell/oto_agent_bootstrap.sh](/config/workspace/oto/apps/runner/assets/script/shell/oto_agent_bootstrap.sh:132)는 `edge:` config를 생성한다.

해결 방법: `--server-url`을 표준 flag로 추가하고 config는 `server: url:`을 우선 작성한다. migration compatibility가 필요하면 `--edge-url`은 alias로 받아 같은 변수에 매핑한다.

Before:

```bash
16 echo "Usage: $0 --edge-url <url> --agent-id <id> --enrollment-token <token> --release-base-url <url> [options]"
28 --edge-url)
132 edge:
133   url: "$edge_url"
```

After:

```bash
echo "Usage: $0 --server-url <url> --agent-id <id> --enrollment-token <token> --release-base-url <url> [options]"
--server-url|--edge-url)
  server_url="$2"
server:
  url: "$server_url"
```

수정 파일 및 체크리스트:

- [ ] `apps/runner/assets/script/shell/oto_agent_bootstrap.sh`
- [ ] `apps/runner/test/oto_agent_bootstrap_script_test.dart`

테스트 작성: Dart script contract/functional tests 갱신. `--server-url` required, `--edge-url` alias 여부, token non-leak, PID path 유지.

중간 검증:

```bash
cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart
```

### [API-4] Config parser와 CLI help

문제: [apps/runner/lib/oto/agent/agent_config.dart](/config/workspace/oto/apps/runner/lib/oto/agent/agent_config.dart:125)는 `edge` section만 읽고, [apps/runner/lib/cli/commands/command_agent.dart](/config/workspace/oto/apps/runner/lib/cli/commands/command_agent.dart:96)는 Edge server를 말한다.

해결 방법: `server` section을 우선 읽고 기존 `edge` section은 migration fallback으로 지원한다. CLI/help/log는 OTO Server로 바꾼다.

수정 파일 및 체크리스트:

- [ ] `apps/runner/lib/oto/agent/agent_config.dart`
- [ ] `apps/runner/lib/cli/commands/command_agent.dart`
- [ ] `apps/runner/lib/oto/agent/agent_runner.dart`
- [ ] `apps/runner/test/oto_agent_config_test.dart`
- [ ] `apps/runner/test/oto_agent_cli_test.dart`

테스트 작성: config parser tests에 `server:` 표준, `edge:` fallback, missing server/edge error를 추가한다.

중간 검증:

```bash
cd apps/runner && dart test test/oto_agent_config_test.dart test/oto_agent_cli_test.dart
```

### [API-5] Full bootstrap smoke

문제: [apps/runner/test/oto_agent_bootstrap_script_test.dart](/config/workspace/oto/apps/runner/test/oto_agent_bootstrap_script_test.dart:77)는 script archive/PID behavior는 검증하지만 server-issued command와 연결하지 않는다.

해결 방법: Go endpoint response command와 shell script contract가 같은 flag 이름을 쓰는지 검증한다. 외부 release artifact 없이 fake curl/tar fixture를 유지한다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/httpserver/server_test.go`
- [ ] `apps/runner/test/oto_agent_bootstrap_script_test.dart`
- [ ] `agent-test/local/agent-smoke.md`

테스트 작성: Go command rendering test와 Dart script functional test를 둘 다 추가한다.

중간 검증:

```bash
cd services/core && go test ./internal/httpserver
cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/httpserver/server.go` | API-2 |
| `services/core/internal/httpserver/server_test.go` | API-2, API-5 |
| `services/core/internal/runnerregistry/registry.go` | API-2 |
| `apps/runner/assets/script/shell/oto_agent_bootstrap.sh` | API-3 |
| `apps/runner/test/oto_agent_bootstrap_script_test.dart` | API-3, API-5 |
| `apps/runner/lib/oto/agent/agent_config.dart` | API-4 |
| `apps/runner/lib/cli/commands/command_agent.dart` | API-4 |
| `apps/runner/lib/oto/agent/agent_runner.dart` | API-4 |
| `apps/runner/test/oto_agent_config_test.dart` | API-4 |
| `apps/runner/test/oto_agent_cli_test.dart` | API-4 |
| `agent-test/local/agent-smoke.md` | API-5 |

## 최종 검증

```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_iop_connection_smoke_test.dart
```

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