Feature Requirements: container-execution
Metadata
- Feature: container-execution
- Status: APPROVED
- Approved: 2026-01-28
- Created: 2026-01-28
- Author: ZERG Plan Mode
1. Problem Statement
1.1 Background
ZERG workers currently only run as local subprocesses. The ContainerLauncher class is fully implemented (~550 LOC) but has never been exercised end-to-end. The Docker image (zerg-worker) has never been built. The existing plan (claudedocs/plan-container-dogfooding.md) outlines build-and-verify steps but doesn't cover: resource limits, OAuth credential passthrough, health checks, cross-platform compatibility, or production hardening.
1.2 Problem
- Docker image doesn't exist —
docker images | grep zerg-worker returns nothing
- ContainerLauncher has never been run against real Docker — only unit-tested with mocked subprocess calls
- No resource limits on containers — a runaway worker could consume all host memory/CPU
- OAuth credential mounting is implemented but untested — users without API keys can't use container mode
- No health check or auto-recovery for container workers
- No log collection from container workers to host
docker-compose.yaml defines resource limits (4G/2CPU) but ContainerLauncher bypasses compose entirely
privileged: true in docker-compose.yaml is a security concern
1.3 Impact
Without container mode, ZERG cannot provide worker isolation. Subprocess workers share the host filesystem, environment, and can interfere with each other or the user's system. Container mode is the security boundary that makes parallel execution safe.
2. Users
2.1 Primary Users
Developers running ZERG on macOS (Docker Desktop, ARM64) or Linux (native Docker) to execute parallel feature builds with container isolation.
2.2 User Stories
- As a developer, I want to run
zerg rush --mode container and have workers execute in isolated Docker containers so that parallel workers can't interfere with each other
- As a developer, I want container workers to authenticate with Claude Code via my existing OAuth session so I don't need to manage API keys
- As a developer, I want container workers to have resource limits so a runaway worker can't crash my machine
- As a developer, I want to see container worker logs via
zerg logs so I can debug failures
3. Functional Requirements
3.1 Core Capabilities
| ID |
Requirement |
Priority |
Notes |
| FR-001 |
Docker image builds successfully on macOS ARM64 and Linux x86_64 |
Must |
Multi-platform support |
| FR-002 |
zerg rush --mode container --workers N spawns N container workers |
Must |
Core execution path |
| FR-003 |
Container workers execute tasks, commit changes, report status |
Must |
Full task lifecycle |
| FR-004 |
Resource limits (memory, CPU) applied per container |
Must |
Default: 4G RAM, 2 CPU |
| FR-005 |
ANTHROPIC_API_KEY passthrough to containers |
Must |
Primary auth method |
| FR-006 |
OAuth credential passthrough via ~/.claude mount |
Must |
Secondary auth method |
| FR-007 |
Git worktree operations work inside containers |
Must |
Commits, branches, pushes |
| FR-008 |
Worker state (RUNNING/STOPPED/CRASHED) visible via zerg status |
Must |
Uses state file IPC (just implemented) |
| FR-009 |
Container cleanup on worker exit (success, failure, or crash) |
Must |
No orphan containers |
| FR-010 |
zerg rush --mode auto detects image and selects container mode |
Should |
Falls back to subprocess |
| FR-011 |
Container worker logs accessible via docker logs and zerg logs |
Should |
Log collection |
| FR-012 |
Health check detects stuck containers and marks them CRASHED |
Should |
Timeout-based |
| FR-013 |
zerg build command to build the Docker image |
Could |
Convenience wrapper |
| FR-014 |
Remove privileged: true from docker-compose.yaml |
Must |
Security hardening |
3.2 Inputs
zerg rush --mode container --workers N --feature <name>
- ANTHROPIC_API_KEY or OAuth credentials in ~/.claude
- Docker daemon running and accessible
3.3 Outputs
- N running containers named
zerg-worker-0..N-1
- Task results committed to worker branches in worktrees
- Worker state in
.zerg/state/{feature}.json
- Container logs accessible via
docker logs zerg-worker-N
3.4 Business Rules
- Container workers MUST NOT have access to ~/.ssh, ~/.aws, or ~/.config (only ~/.claude)
- Container workers MUST run as non-root (current UID/GID mapping already implemented)
- Containers MUST be cleaned up even on orchestrator crash (use
--rm or explicit cleanup)
4. Non-Functional Requirements
4.1 Performance
- Container spawn: <30s per worker (image pull excluded)
- Container overhead vs subprocess: <5% task execution time penalty
- Resource limits: 4G RAM, 2 CPU per container (configurable via .zerg/config.yaml)
4.2 Security
- No privileged mode
- Non-root execution (UID/GID pass-through)
- Mount only: worktree, .zerg/state, ~/.claude, .git (for commits)
- No host network mode — use bridge network
- Environment variable allowlist enforced (existing validate_env_vars)
4.3 Reliability
- Orphan container detection and cleanup on orchestrator start
- Container health check via
docker inspect in poll loop
- Graceful shutdown: SIGTERM → 10s wait → SIGKILL (already implemented)
4.4 Platform Support
- macOS Docker Desktop (ARM64) — primary
- Linux native Docker (x86_64) — secondary
- No Windows support required
5. Scope
5.1 In Scope
- Build and verify Docker image
- E2E dogfood: plan → design → rush --mode container → status → merge
- Resource limits on containers
- OAuth credential passthrough
- Cross-platform Dockerfile (multi-arch)
- Fix security issues (remove privileged, constrain mounts)
- Integration tests with real Docker (gated on CI Docker availability)
- Log collection from containers
- Health check and auto-recovery for stuck workers
5.2 Out of Scope
- Docker-in-Docker (Workflow B: container spawning containers) — deferred
- docker-compose orchestration — keep using docker run
- wait-for-services.sh (postgres/redis profiles) — separate feature
- Dynamic devcontainer generation from devcontainer_features.py — separate feature
- Kubernetes/remote Docker host support — future
5.3 Assumptions
- Docker daemon is running and accessible via
docker CLI
- User has built the image (or
zerg build will build it)
- ANTHROPIC_API_KEY is set OR user has valid OAuth session in ~/.claude
5.4 Constraints
- Must not break existing subprocess mode
- Must use existing ContainerLauncher API (no interface changes)
- Must work with existing state file IPC (just implemented)
6. Dependencies
6.1 Internal Dependencies
| Dependency |
Type |
Status |
| State file IPC |
Required |
Complete (a189fc7) |
| ContainerLauncher class |
Required |
Implemented, untested E2E |
| SubprocessLauncher |
Required |
Working, must not regress |
| worker_entry.sh |
Required |
Implemented, untested in real container |
6.2 External Dependencies
| Dependency |
Type |
Owner |
| Docker daemon |
Required |
User |
| Docker Desktop (macOS) |
Required |
User |
| ANTHROPIC_API_KEY or OAuth |
Required |
User |
| Claude Code CLI (npm) |
Required |
Dockerfile |
7. Acceptance Criteria
7.1 Definition of Done
7.2 Test Scenarios
| ID |
Scenario |
Given |
When |
Then |
| TC-001 |
Image build |
Dockerfile exists |
docker build -t zerg-worker . |
Image built, claude --version works |
| TC-002 |
Container spawn |
Image exists |
zerg rush --mode container --dry-run |
Dry run shows container mode |
| TC-003 |
Full E2E |
Feature planned+designed |
zerg rush --mode container --workers 2 |
Tasks complete, branches merged |
| TC-004 |
State visibility |
Workers running |
zerg status from another terminal |
Worker data appears |
| TC-005 |
Resource limits |
Container running |
docker inspect zerg-worker-0 |
Memory=4G, CPU=2 |
| TC-006 |
Cleanup on crash |
Worker crashes |
Orchestrator poll loop runs |
Container removed, state=CRASHED |
| TC-007 |
OAuth auth |
~/.claude has valid session |
Container worker starts |
Claude Code authenticates |
| TC-008 |
API key auth |
ANTHROPIC_API_KEY set |
Container worker starts |
Claude Code authenticates |
| TC-009 |
No orphans |
Rush completes |
docker ps -a | grep zerg-worker |
No containers left |
| TC-010 |
Subprocess unaffected |
No Docker |
zerg rush --mode subprocess |
Works as before |
7.3 Success Metrics
- Container workers complete tasks at same success rate as subprocess workers
- Zero orphan containers after any rush completion
zerg status shows live worker data during container execution
8. Open Questions
| ID |
Question |
Status |
| Q-001 |
Should zerg build auto-detect platform and build multi-arch? |
Open |
| Q-002 |
Should resource limits be configurable in .zerg/config.yaml? |
Open (default to yes) |
| Q-003 |
Should we add --auto-build flag to zerg rush to build image if missing? |
Open |
9. Approval
| Role |
Name |
Date |
Signature |
| Product |
|
|
PENDING |
| Engineering |
|
|
PENDING |
1---2name: feature-requirements-container-execution3description: ZERG workers currently only run as local subprocesses. The ContainerLauncher class is fully implemented (~550 LOC) but has never been exercised end-to-end. The Docker image (zerg-worker) has never been built.4---5# Feature Requirements: container-execution67## Metadata8- **Feature**: container-execution9- **Status**: APPROVED10- **Approved**: 2026-01-2811- **Created**: 2026-01-2812- **Author**: ZERG Plan Mode1314---1516## 1. Problem Statement1718### 1.1 Background19ZERG workers currently only run as local subprocesses. The ContainerLauncher class is fully implemented (~550 LOC) but has never been exercised end-to-end. The Docker image (`zerg-worker`) has never been built. The existing plan (`claudedocs/plan-container-dogfooding.md`) outlines build-and-verify steps but doesn't cover: resource limits, OAuth credential passthrough, health checks, cross-platform compatibility, or production hardening.2021### 1.2 Problem22- Docker image doesn't exist — `docker images | grep zerg-worker` returns nothing23- ContainerLauncher has never been run against real Docker — only unit-tested with mocked subprocess calls24- No resource limits on containers — a runaway worker could consume all host memory/CPU25- OAuth credential mounting is implemented but untested — users without API keys can't use container mode26- No health check or auto-recovery for container workers27- No log collection from container workers to host28- `docker-compose.yaml` defines resource limits (4G/2CPU) but ContainerLauncher bypasses compose entirely29- `privileged: true` in docker-compose.yaml is a security concern3031### 1.3 Impact32Without container mode, ZERG cannot provide worker isolation. Subprocess workers share the host filesystem, environment, and can interfere with each other or the user's system. Container mode is the security boundary that makes parallel execution safe.3334---3536## 2. Users3738### 2.1 Primary Users39Developers running ZERG on macOS (Docker Desktop, ARM64) or Linux (native Docker) to execute parallel feature builds with container isolation.4041### 2.2 User Stories42- As a developer, I want to run `zerg rush --mode container` and have workers execute in isolated Docker containers so that parallel workers can't interfere with each other43- As a developer, I want container workers to authenticate with Claude Code via my existing OAuth session so I don't need to manage API keys44- As a developer, I want container workers to have resource limits so a runaway worker can't crash my machine45- As a developer, I want to see container worker logs via `zerg logs` so I can debug failures4647---4849## 3. Functional Requirements5051### 3.1 Core Capabilities5253| ID | Requirement | Priority | Notes |54|----|-------------|----------|-------|55| FR-001 | Docker image builds successfully on macOS ARM64 and Linux x86_64 | Must | Multi-platform support |56| FR-002 | `zerg rush --mode container --workers N` spawns N container workers | Must | Core execution path |57| FR-003 | Container workers execute tasks, commit changes, report status | Must | Full task lifecycle |58| FR-004 | Resource limits (memory, CPU) applied per container | Must | Default: 4G RAM, 2 CPU |59| FR-005 | ANTHROPIC_API_KEY passthrough to containers | Must | Primary auth method |60| FR-006 | OAuth credential passthrough via ~/.claude mount | Must | Secondary auth method |61| FR-007 | Git worktree operations work inside containers | Must | Commits, branches, pushes |62| FR-008 | Worker state (RUNNING/STOPPED/CRASHED) visible via `zerg status` | Must | Uses state file IPC (just implemented) |63| FR-009 | Container cleanup on worker exit (success, failure, or crash) | Must | No orphan containers |64| FR-010 | `zerg rush --mode auto` detects image and selects container mode | Should | Falls back to subprocess |65| FR-011 | Container worker logs accessible via `docker logs` and `zerg logs` | Should | Log collection |66| FR-012 | Health check detects stuck containers and marks them CRASHED | Should | Timeout-based |67| FR-013 | `zerg build` command to build the Docker image | Could | Convenience wrapper |68| FR-014 | Remove `privileged: true` from docker-compose.yaml | Must | Security hardening |6970### 3.2 Inputs71- `zerg rush --mode container --workers N --feature <name>`72- ANTHROPIC_API_KEY or OAuth credentials in ~/.claude73- Docker daemon running and accessible7475### 3.3 Outputs76- N running containers named `zerg-worker-0..N-1`77- Task results committed to worker branches in worktrees78- Worker state in `.zerg/state/{feature}.json`79- Container logs accessible via `docker logs zerg-worker-N`8081### 3.4 Business Rules82- Container workers MUST NOT have access to ~/.ssh, ~/.aws, or ~/.config (only ~/.claude)83- Container workers MUST run as non-root (current UID/GID mapping already implemented)84- Containers MUST be cleaned up even on orchestrator crash (use `--rm` or explicit cleanup)8586---8788## 4. Non-Functional Requirements8990### 4.1 Performance91- Container spawn: <30s per worker (image pull excluded)92- Container overhead vs subprocess: <5% task execution time penalty93- Resource limits: 4G RAM, 2 CPU per container (configurable via .zerg/config.yaml)9495### 4.2 Security96- No privileged mode97- Non-root execution (UID/GID pass-through)98- Mount only: worktree, .zerg/state, ~/.claude, .git (for commits)99- No host network mode — use bridge network100- Environment variable allowlist enforced (existing validate_env_vars)101102### 4.3 Reliability103- Orphan container detection and cleanup on orchestrator start104- Container health check via `docker inspect` in poll loop105- Graceful shutdown: SIGTERM → 10s wait → SIGKILL (already implemented)106107### 4.4 Platform Support108- macOS Docker Desktop (ARM64) — primary109- Linux native Docker (x86_64) — secondary110- No Windows support required111112---113114## 5. Scope115116### 5.1 In Scope117- Build and verify Docker image118- E2E dogfood: plan → design → rush --mode container → status → merge119- Resource limits on containers120- OAuth credential passthrough121- Cross-platform Dockerfile (multi-arch)122- Fix security issues (remove privileged, constrain mounts)123- Integration tests with real Docker (gated on CI Docker availability)124- Log collection from containers125- Health check and auto-recovery for stuck workers126127### 5.2 Out of Scope128- Docker-in-Docker (Workflow B: container spawning containers) — deferred129- docker-compose orchestration — keep using docker run130- wait-for-services.sh (postgres/redis profiles) — separate feature131- Dynamic devcontainer generation from devcontainer_features.py — separate feature132- Kubernetes/remote Docker host support — future133134### 5.3 Assumptions135- Docker daemon is running and accessible via `docker` CLI136- User has built the image (or `zerg build` will build it)137- ANTHROPIC_API_KEY is set OR user has valid OAuth session in ~/.claude138139### 5.4 Constraints140- Must not break existing subprocess mode141- Must use existing ContainerLauncher API (no interface changes)142- Must work with existing state file IPC (just implemented)143144---145146## 6. Dependencies147148### 6.1 Internal Dependencies149| Dependency | Type | Status |150|------------|------|--------|151| State file IPC | Required | Complete (a189fc7) |152| ContainerLauncher class | Required | Implemented, untested E2E |153| SubprocessLauncher | Required | Working, must not regress |154| worker_entry.sh | Required | Implemented, untested in real container |155156### 6.2 External Dependencies157| Dependency | Type | Owner |158|------------|------|-------|159| Docker daemon | Required | User |160| Docker Desktop (macOS) | Required | User |161| ANTHROPIC_API_KEY or OAuth | Required | User |162| Claude Code CLI (npm) | Required | Dockerfile |163164---165166## 7. Acceptance Criteria167168### 7.1 Definition of Done169- [ ] Docker image builds on macOS ARM64170- [ ] Docker image builds on Linux x86_64171- [ ] `zerg rush --mode container --workers 2 --dry-run` completes172- [ ] `zerg rush --mode container --workers 2` executes a real feature173- [ ] Workers write state visible via `zerg status` in another terminal174- [ ] Workers commit code to their branches175- [ ] Merge succeeds after level completion176- [ ] Resource limits applied (verify via `docker inspect`)177- [ ] No orphan containers after rush completes178- [ ] OAuth authentication works in containers179- [ ] All existing tests pass (no subprocess regression)180- [ ] New integration tests for container mode pass181- [ ] privileged: true removed from docker-compose.yaml182183### 7.2 Test Scenarios184185| ID | Scenario | Given | When | Then |186|----|----------|-------|------|------|187| TC-001 | Image build | Dockerfile exists | `docker build -t zerg-worker .` | Image built, claude --version works |188| TC-002 | Container spawn | Image exists | `zerg rush --mode container --dry-run` | Dry run shows container mode |189| TC-003 | Full E2E | Feature planned+designed | `zerg rush --mode container --workers 2` | Tasks complete, branches merged |190| TC-004 | State visibility | Workers running | `zerg status` from another terminal | Worker data appears |191| TC-005 | Resource limits | Container running | `docker inspect zerg-worker-0` | Memory=4G, CPU=2 |192| TC-006 | Cleanup on crash | Worker crashes | Orchestrator poll loop runs | Container removed, state=CRASHED |193| TC-007 | OAuth auth | ~/.claude has valid session | Container worker starts | Claude Code authenticates |194| TC-008 | API key auth | ANTHROPIC_API_KEY set | Container worker starts | Claude Code authenticates |195| TC-009 | No orphans | Rush completes | `docker ps -a \| grep zerg-worker` | No containers left |196| TC-010 | Subprocess unaffected | No Docker | `zerg rush --mode subprocess` | Works as before |197198### 7.3 Success Metrics199- Container workers complete tasks at same success rate as subprocess workers200- Zero orphan containers after any rush completion201- `zerg status` shows live worker data during container execution202203---204205## 8. Open Questions206207| ID | Question | Status |208|----|----------|--------|209| Q-001 | Should `zerg build` auto-detect platform and build multi-arch? | Open |210| Q-002 | Should resource limits be configurable in .zerg/config.yaml? | Open (default to yes) |211| Q-003 | Should we add `--auto-build` flag to `zerg rush` to build image if missing? | Open |212213---214215## 9. Approval216217| Role | Name | Date | Signature |218|------|------|------|-----------|219| Product | | | PENDING |220| Engineering | | | PENDING |