<!-- task=m-cross-os-runner-bootstrap/03+01_windows_bootstrap plan=0 tag=BOOTSTRAP_WINDOWS -->

# CODE REVIEW: Windows PowerShell bootstrap 추가

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-product-surface/milestones/cross-os-runner-bootstrap.md`
- Epic: `[bootstrap-platforms] Cross-OS bootstrap contract`
- Task id: `[windows-bootstrap]`
- Completion mode: check-on-pass

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

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

## 구현 에이전트 소유 섹션

### 계획 대비 변경 사항

- `runner_handlers.go`와 `server_test.go`의 Windows command 생성 로직(사례 11, 12, 13)은 `01_core_command`에서 이미 구현 완료되어 있었다. 이번 작업에서는 touch하지 않았다.
- Dart 테스트는 PowerShell을 Linux 환경에서 실행할 수 없으므로 content assertion 전용 파일(`oto_agent_bootstrap_ps1_test.dart`)을 별도 생성했다. PLAN의 "또는 별도 runner bootstrap test" 선택지를 따랐다.
- `oto-windows-x64.zip` / `oto-windows-arm64.zip` 리터럴이 스크립트에 존재하지 않고 `"oto-windows-$archName.zip"` 패턴으로 동적 생성되어, Dart 테스트를 패턴 검증으로 조정했다. 쉘 스크립트의 `oto-${os_name}-${arch_name}.tar.gz` 패턴과 동일한 방식이다.

### 수정 파일

| 경로 | 목적 |
|------|------|
| `apps/runner/assets/script/powershell/oto_agent_bootstrap.ps1` | Windows bootstrap 구현 (신규 생성) |
| `services/core/internal/httpserver/oto_agent_bootstrap.ps1` | Core embedded Windows asset (신규 생성, 동일 내용) |
| `services/core/internal/httpserver/bootstrap_provider.go` | `bootstrapPs1Provider` 인터페이스 및 `embeddedBootstrapPs1Provider` 추가 |
| `services/core/internal/httpserver/runner_handlers.go` | `handleServeBootstrapPs1` handler 추가 |
| `services/core/internal/httpserver/routes.go` | `/bootstrap/oto-agent.ps1` route 추가 |
| `services/core/internal/httpserver/server_test.go` | `staticPs1Provider`, PS1 serve/route/drift check 테스트 추가 |
| `apps/runner/test/oto_agent_bootstrap_ps1_test.dart` | PowerShell script content assertion 테스트 (신규 생성) |

### 검증 결과

```
# Go 전체
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/cicdstate    (cached)
ok  github.com/toki/oto/services/core/internal/httpserver   ok
ok  github.com/toki/oto/services/core/internal/runnerregistry (cached)
?   github.com/toki/oto/services/core/oto            [no test files]

# Dart shell test (기존 19개 유지)
cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart
+19: All tests passed!

# Dart PS1 content tests (신규 9개)
cd apps/runner && dart test test/oto_agent_bootstrap_ps1_test.dart
+9: All tests passed!

# Dart analyze
cd apps/runner && dart analyze
No issues found!
```

### 구현 체크리스트

- [x] PowerShell bootstrap asset을 추가하고 Windows x64/arm64 zip asset 선택을 고정한다.
- [x] Core가 `.ps1` bootstrap asset을 serve하고 Windows command에서 해당 URL을 사용한다.
- [x] script/config 생성 시 기존 agent 설정 의미를 유지하고 enrollment token 출력 노출을 피한다.
- [x] Windows route, content, command, quoting, zip asset 이름을 테스트한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드 리뷰 전용 섹션

- [x] `01_core_command` 완료 근거가 있다. (`agent-task/archive/2026/06/m-cross-os-runner-bootstrap/01_core_command/complete.log` 존재 확인)
- [x] PowerShell quoting과 Windows path가 fixture로 검증된다. (`server_test.go` cases 11/12, Dart PS1 content tests)
- [x] `.ps1` serving route와 command URL이 일치한다. (`/bootstrap/oto-agent.ps1` route + command의 `oto-agent.ps1` URL)
- [x] token/credential 출력 노출이 없다. (Dart 테스트 + PS1 script에서 Write-Host가 token 변수를 출력하지 않음)

## 완료 판정 메모

| 항목 | 값 |
|------|----|
| Verdict | PASS |
| Roadmap Completion | `[windows-bootstrap]` only |
| Archive ready | yes |

## 코드리뷰 결과

### 종합 판정

FAIL

### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| correctness | Fail | Windows background 실행과 config credential 보호에 실제 동작 결함 가능성이 남아 있다. |
| completeness | Fail | 계획의 Windows bootstrap 계약은 추가됐지만 agent 도메인 규칙의 config 보호 의미와 background 실행 계약이 완결되지 않았다. |
| test coverage | Fail | PowerShell content assertion은 있으나 background log redirection과 config ACL/permission 보호를 고정하는 테스트가 없다. |
| API contract | Pass | `.ps1` route와 Windows bootstrap command URL 계약은 일치한다. |
| code quality | Pass | 관련 Go handler와 drift test는 기존 패턴을 따른다. |
| plan deviation | Pass | 별도 PS1 content assertion 테스트 생성은 계획의 허용 범위 안이다. |
| verification trust | Warn | 재실행한 Go/Dart 검증은 통과했지만 Linux content assertion만으로 Windows runtime 의미를 검증하지 못한다. |

### 발견된 문제

- Required: `apps/runner/assets/script/powershell/oto_agent_bootstrap.ps1:100` 및 mirrored `services/core/internal/httpserver/oto_agent_bootstrap.ps1:100`에서 `Start-Process`가 `-RedirectStandardOutput`과 `-RedirectStandardError`에 같은 `$logFile`을 넘긴다. Windows PowerShell은 두 redirect 대상이 같으면 process start가 실패할 수 있어 기본 background bootstrap이 깨질 수 있다. stdout/stderr를 별도 파일로 분리하거나 PowerShell redirection wrapper로 단일 로그를 만들고, PS1 content test에 같은 경로를 동시에 쓰지 않는다는 assertion을 추가한다.
- Required: `apps/runner/assets/script/powershell/oto_agent_bootstrap.ps1:93` 및 mirrored `services/core/internal/httpserver/oto_agent_bootstrap.ps1:93`에서 enrollment token이 들어간 config를 쓴 뒤 Windows ACL/permission을 제한하지 않는다. agent 도메인 규칙은 생성 config를 `chmod 600` 의미로 보호해야 하므로, Windows equivalent ACL 적용을 추가하고 테스트가 `SetAccessRuleProtection`/`icacls` 등 permission hardening을 확인하게 한다.

### 다음 단계

- FAIL 후속: active plan/review를 로그로 아카이브하고, 위 Required 2건만 다루는 `PLAN-cloud-G07.md` 및 `CODE_REVIEW-cloud-G07.md`를 새로 작성한다.

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

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_0.log`로 아카이브한다.
- [x] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_0.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-cross-os-runner-bootstrap/03+01_windows_bootstrap/`를 `agent-task/archive/YYYY/MM/m-cross-os-runner-bootstrap/03+01_windows_bootstrap/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-cross-os-runner-bootstrap/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
