# Bootstrap script distribution boundary

## Metadata

- Status: planned
- Task group: `control_plane_phase_exit_followups`
- Plan id: `local-G05`
- Review stub: `CODE_REVIEW-local-G05.md`
- Roadmap target: first phase exit follow-up for `agent-roadmap/phase/independent-control-plane/PHASE.md`

## Problem

`handleServeBootstrapScript` serves `oto_agent_bootstrap.sh` by trying source checkout-relative paths such as `../../../apps/runner/assets/script/shell/oto_agent_bootstrap.sh`.

That works in local source-tree tests, but it is fragile for a built core binary, packaged deployment, or any runtime whose working directory is not the repository layout.

## Rules read

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/project/domain/framework/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`

## Current evidence

- `services/core/internal/httpserver/runner_handlers.go`
  - `handleServeBootstrapScript` builds a candidate path list from the current working directory.
  - It returns 500 if the script is not found at those source-relative locations.
- `apps/runner/assets/script/shell/oto_agent_bootstrap.sh`
  - The script itself is runner-owned and exists.
- `services/core/internal/httpserver/server_test.go`
  - HTTP handler tests already live near this boundary.

## Scope

Make bootstrap script serving independent from repository working directory assumptions.

Do not redesign enrollment tokens, install semantics, or runner bootstrap script content in this task.

## Proposed implementation

1. Introduce an explicit script provider boundary.
   - The HTTP handler should receive script bytes from a provider instead of discovering source paths itself.
   - Tests should inject a provider with deterministic content.

2. Choose a production source of truth.
   - Preferred: embed a core-owned packaged copy with Go `embed`, and add a drift check against `apps/runner/assets/script/shell/oto_agent_bootstrap.sh`.
   - Acceptable alternative: require an explicit configured path such as `OTO_BOOTSTRAP_SCRIPT_PATH`, with clear startup/handler failure behavior.
   - Avoid silent fallback to source checkout paths for production behavior.

3. Preserve response behavior.
   - `Content-Type` should remain shell script friendly.
   - The handler should still return useful errors when content is unavailable.
   - Existing bootstrap endpoint paths should not change.

4. Add tests.
   - Provider-backed success response.
   - Missing provider/content failure response.
   - If embedding/copying is used, add a drift test comparing embedded/core copy with runner asset or document the sync step in the test name.

## Expected files

- `services/core/internal/httpserver/runner_handlers.go`
- `services/core/internal/httpserver/server.go`
- `services/core/internal/httpserver/server_test.go`
- Possible new asset under `services/core/internal/httpserver/`
- Possibly `Makefile` if a drift check or sync command is added.

## Verification

- `cd services/core && go test -count=1 ./...`
- If a Makefile drift check is added, run the new target as well.

## Risks

- Duplicating the shell script can drift unless guarded by a test or generation step.
- Requiring an env/config path can make local dev more explicit but less convenient.
- Embedding across module boundaries is not supported directly by Go `embed`, so the production source needs to live under the core module or be provided at runtime.

## Done when

- A built core binary can serve the bootstrap script without relying on repository-relative cwd.
- Handler tests cover the provider behavior.
- Core tests pass locally.
