Files
pirate-station/.planning/phases/01-foundation/01-02-PLAN.md
acty ccb93eda21 docs(01): create phase 1 foundation plans
Phase 01: Foundation
- 2 plans in 2 waves
- Plan 01: Go project + Dockerfile (wave 1)
- Plan 02: Dev environment + verification (wave 2)
- Ready for execution

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 16:56:50 +09:00

293 lines
8.7 KiB
Markdown

---
phase: 01-foundation
plan: 02
type: execute
wave: 2
depends_on: ["01-01"]
files_modified:
- docker-compose.yml
- .air.toml
autonomous: false
must_haves:
truths:
- "Developer can start local environment with single command"
- "Code changes trigger automatic rebuild"
- "Container can only access /data volume, not host filesystem"
- "Health endpoint returns healthy when /data mounted"
- "Health endpoint returns unhealthy when /data not mounted"
artifacts:
- path: "docker-compose.yml"
provides: "Development environment orchestration"
contains: "services:"
- path: ".air.toml"
provides: "Hot reload configuration"
contains: "[build]"
key_links:
- from: "docker-compose.yml"
to: "docker/Dockerfile"
via: "build context"
pattern: "dockerfile.*docker/Dockerfile"
- from: "docker-compose.yml"
to: ".air.toml"
via: "air command"
pattern: "air.*\\.air\\.toml"
---
<objective>
Set up Docker Compose development environment with hot reload and verify complete foundation.
Purpose: Creates the local development workflow so changes are automatically rebuilt. Then verifies the entire Phase 1 deliverable works end-to-end: container isolation, HTTP serving, and health checks.
Output: Working development environment with hot reload; verified Phase 1 requirements (INFRA-01, INFRA-02).
</objective>
<execution_context>
@/home/acty/.claude/get-shit-done/workflows/execute-plan.md
@/home/acty/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/01-foundation/01-CONTEXT.md
@.planning/phases/01-foundation/01-RESEARCH.md
@.planning/phases/01-foundation/01-01-SUMMARY.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Create Docker Compose development environment</name>
<files>
docker-compose.yml
.air.toml
</files>
<action>
1. Create docker-compose.yml at project root:
```yaml
services:
backend:
build:
context: .
dockerfile: docker/Dockerfile
target: builder # Stop at build stage for dev
command: air -c .air.toml
ports:
- "32768:32768"
volumes:
- .:/workspace:cached # Bind mount for live editing
- data:/data # Named volume for persistent data
working_dir: /workspace
environment:
- CGO_ENABLED=0
volumes:
data:
```
2. Create .air.toml for hot reload:
```toml
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/server"
cmd = "go build -o ./tmp/server ./cmd/server"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", ".planning", "docker", ".git"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true
```
Key points:
- target: builder uses build stage (has Go toolchain)
- Bind mount .:/workspace for live editing
- Named volume data:/data for persistent storage
- Air watches for .go file changes and rebuilds
</action>
<verify>
```bash
docker compose config && echo "Compose config valid"
```
</verify>
<done>
- docker-compose.yml exists and is valid YAML
- .air.toml configures hot reload for Go files
- Development environment can be started with `docker compose up`
</done>
</task>
<task type="auto">
<name>Task 2: Verify container isolation and health endpoint</name>
<files></files>
<action>
1. Build production image:
```bash
docker build -f docker/Dockerfile -t pirate-station:latest .
```
2. Test container isolation - verify container cannot access host filesystem:
```bash
# Create a test file outside of data volume
echo "secret" > /tmp/host-secret.txt
# Run container with data volume and try to access host file
docker run --rm -d --name isolation-test -p 32768:32768 -v pirate-test-data:/data pirate-station:latest
# Container should NOT be able to see /tmp/host-secret.txt
# (It's isolated to its own filesystem + /data volume only)
```
3. Test health endpoint with volume mounted:
```bash
# Wait for container to start
sleep 2
# Health check should return healthy
curl -s http://localhost:32768/health
# Expected: {"status":"healthy"}
# Root endpoint should return API message
curl -s http://localhost:32768/
# Expected: Pirate Station API
# Stop test container
docker stop isolation-test
docker volume rm pirate-test-data
```
4. Test health endpoint WITHOUT volume (should be unhealthy):
```bash
# Run container without /data volume mounted
docker run --rm -d --name no-data-test -p 32769:32768 pirate-station:latest
sleep 2
# Health check should return unhealthy
curl -s http://localhost:32769/health
# Expected: {"status":"unhealthy","reason":"data volume not mounted"}
docker stop no-data-test
```
5. Verify multi-arch build capability (don't push, just verify it works):
```bash
# Create builder if not exists
docker buildx create --name pirate-builder --use 2>/dev/null || docker buildx use pirate-builder
# Build for both platforms (local only, no push)
docker buildx build --platform linux/amd64,linux/arm64 -f docker/Dockerfile -t pirate-station:multiarch .
```
</action>
<verify>
```bash
# All tests pass if:
# 1. curl localhost:32768/health returns {"status":"healthy"}
# 2. curl localhost:32769/health returns unhealthy
# 3. buildx multi-arch build completes
echo "Verification requires running the tests in <action>"
```
</verify>
<done>
- Production image builds successfully
- Container isolation verified (cannot access host filesystem)
- Health endpoint returns healthy with /data mounted
- Health endpoint returns unhealthy without /data
- Multi-arch build works for amd64 and arm64
</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
Complete Phase 1 foundation:
- Go HTTP server on port 32768
- Multi-stage Dockerfile with Debian slim runtime
- Docker Compose dev environment with hot reload
- Health check endpoint that verifies /data volume
</what-built>
<how-to-verify>
1. Start dev environment: `docker compose up --build`
2. In another terminal, test endpoints:
- `curl http://localhost:32768/` should return "Pirate Station API"
- `curl http://localhost:32768/health` should return {"status":"healthy"}
3. Edit cmd/server/main.go (change the root message text)
4. Watch terminal - Air should detect change and rebuild
5. `curl http://localhost:32768/` should show your change
6. Ctrl+C to stop, then `docker compose down`
Expected: Server starts, endpoints respond, hot reload works.
</how-to-verify>
<resume-signal>Type "approved" if dev environment works, or describe any issues</resume-signal>
</task>
</tasks>
<verification>
Phase 1 requirements verification:
**INFRA-01: Docker container runs isolated to mounted volume only**
- Container uses non-root user (appuser)
- Only /data volume is mounted from host
- Container filesystem is isolated (debian slim base)
- Verified by: Cannot access host files outside /data
**INFRA-02: Single binary Go backend**
- Go compiles to single static binary (CGO_ENABLED=0)
- Binary includes all dependencies (stdlib only)
- No runtime dependencies except libc (debian slim provides)
- Verified by: `go build ./cmd/server` produces single executable
**Success criteria from roadmap:**
1. Docker container starts with volume mount and runs Go binary - VERIFIED
2. Container cannot access files outside mounted volume - VERIFIED
3. Go backend serves HTTP endpoint on specified port (32768) - VERIFIED
4. Container can be built on x86 and deployed to ARM64 (Pi) - VERIFIED via buildx
</verification>
<success_criteria>
- `docker compose up` starts dev environment
- Hot reload rebuilds on code changes
- `curl localhost:32768/health` returns healthy JSON
- Production build works for amd64 and arm64
- Container isolation verified (no host filesystem access beyond /data)
- Human verification confirms dev workflow works
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
</output>