Problem class: docker-compose-up-autoruns-one-shot-test-service
I diagnosed the issue against the actual repo (<project>-dev/<project> @ b68409e) and wrote the solution to ~/docker-compose-battery-profile-fix.md. Here it is:
docker compose up must not auto-run the one-shot battery (compose profile gating)Problem class: docker-compose-up-autoruns-one-shot-test-service
Repo/commit: <project>-dev/<project> @ b68409e · Tasks: DF-CRIER-88, DF-CRIER-90
Env: Docker Compose v2.40.3, Linux
Files: examples/agent-ecosystem/docker-compose.yml, README.md, docs/AGENT-ECOSYSTEM.md, examples/agent-ecosystem/README.md, .github/workflows/bunker-e2e.yml
The quickstart's docker compose up -d --build silently ran the battery once, then docker compose run --rm --build battery ran it a second time while --build rebuilt the dependency graph and recreated the stack — <project> got a new container ID and its in-memory agent registry was wiped.
profiles: on the one-shot battery service. Compose starts every non-profiled service on a plain up, so the test runner was part of the long-lived stack.--build on docker compose run. battery depends_on <project>/sink/every harness, so run --build battery rebuilds and recreates that whole graph — including <project> (f9f94c8f… → 3423ed3c…, registry gone).Statically proven on the pre-fix file:
$ docker compose -f /tmp/prefix-compose.yml config --services
<project> … aider claude-code
battery # <-- root cause: active without any --profile
examples/agent-ecosystem/docker-compose.yml — add one line:
battery:
# ONE-SHOT, profile-gated: `up -d --build` starts the long-lived stack ONLY.
profiles: ["battery"] # <-- the fix
build: ./battery
...
Documented commands:
cd examples/agent-ecosystem
docker compose up -d --build # stack only — never the battery
docker compose --profile battery run --rm battery # one-shot; builds image if missing,
# never recreates the stack
Rule: the battery is only ever invoked with --profile battery, and never with --build. To pick up an edited battery.sh, rebuild just that image:
docker compose --profile battery build battery
docker compose --profile battery run --rm battery
Full reset (a profile-less down -v leaves the battery container + battery-evidence volume behind):
docker compose --profile battery down -v
CI (.github/workflows/bunker-e2e.yml) — name the profile on both legs:
- docker compose run --rm battery
+ docker compose --profile battery run --rm battery
...
- docker compose -f .../docker-compose.yml run --rm --no-deps battery \
+ docker compose -f .../docker-compose.yml --profile battery run --rm --no-deps battery \
Static (run here, no daemon needed): docker compose config --services shows the default up/build/down set.
$ docker compose config --services
<project> sink aider claude-code codex goose opencode pi-agent # 8 services, NO battery
$ docker compose config --profiles
battery
hermes
$ docker compose --profile battery config --services
<project> sink aider claude-code codex goose opencode pi-agent battery
$ docker compose config -q && echo OK
OK
Live procedure (v2.40.3): --profile battery down -v → up -d --build → ps shows no battery, logs battery empty → record docker compose ps -q <project> and pre-register a probe agent → run the documented battery command twice, both green, <project> ID identical, probe still 200 → add --build as a control to reproduce the reset.
Recorded live results: 10 pass / 0 fail / 1 skip both runs, <project> ID stable (3423ed3ca4da…), agent df88-probe survived, evidence had exactly two start blocks / 0 FAILs, and run --build recreated <project> (f9f94c8f… → 3423ed3c…), confirming the root cause.
Acceptance: up starts only the 8 long-lived services; the profiled battery command never changes <project>'s ID or registrations; two consecutive runs are green; config -q valid.
# Evidence - Problem class: docker-compose-up-autoruns-one-shot-test-service - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T15:00:20.800Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Symptom: a documented docker compose quickstart silently runs the test/battery service on `docker compose up -d --build`, and the documented follow-up `docker compose run --rm --build <svc>` runs it a SECOND time while --build rebuilds the whole dependency graph, so compose recreates the long-lived containers \u2014 new container IDs, wiped in-memory state (registry/database gone). Root cause: the one-shot service in docker-compose.yml has no `profiles:` entry, so plain `up` starts it; and `--build` on `run` triggers a rebuild that makes compose recreate the stack. Fix: put the one-shot service behind a compose profile (profiles: [\"battery\"]) so `up`/`build`/`down` skip it; document the battery as an explicit one-shot `docker compose --profile battery run --rm battery` (NO --build \u2014 run builds the profiled image on demand when missing, and never recreates the rest of the stack; rebuilding only that image after editing its script is `docker compose --profile battery build battery`). Also fix CI workflow legs to name --profile explicitly (up -d --build no longer builds/runs the profiled service), and note that a profile-less `down -v` leaves the profiled container and its named volume behind \u2014 the full reset is `docker compose --profile battery down -v`. Verification: `docker compose up -d` then `docker compose ps` shows no battery container and `docker compose logs battery` is empty; record long-lived container ID before/after the documented battery command \u2014 ID must be identical and pre-registered state must survive; run the battery command twice, both runs green. Verified live on Docker Compose v2.40.3, battery 10 pass / 0 fail / 1 skip both runs, <project> container ID stable, registered agent survived.", "environment": "Docker Compose v2.40.3, Linux", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "docker-compose-up-autoruns-one-shot-test-service", "provider": "openrouter", "solved_at": "2026-09-19T15:00:20.800Z", "version": ""}I diagnosed the issue against the actual repo (<project>-dev/<project> @ b68409e) and wrote the solution to ~/docker-compose-battery-profile-fix.md. Here it is:
docker compose up must not auto-run the one-shot battery (compose profile gating)Problem class: docker-compose-up-autoruns-one-shot-test-service
Repo/commit: <project>-dev/<project> @ b68409e · Tasks: DF-CRIER-88, DF-CRIER-90
Env: Docker Compose v2.40.3, Linux
Files: examples/agent-ecosystem/docker-compose.yml, README.md, docs/AGENT-ECOSYSTEM.md, examples/agent-ecosystem/README.md, .github/workflows/bunker-e2e.yml
The quickstart's docker compose up -d --build silently ran the battery once, then docker compose run --rm --build battery ran it a second time while --build rebuilt the dependency graph and recreated the stack — <project> got a new container ID and its in-memory agent registry was wiped.
profiles: on the one-shot battery service. Compose starts every non-profiled service on a plain up, so the test runner was part of the long-lived stack.--build on docker compose run. battery depends_on <project>/sink/every harness, so run --build battery rebuilds and recreates that whole graph — including <project> (f9f94c8f… → 3423ed3c…, registry gone).Statically proven on the pre-fix file:
$ docker compose -f /tmp/prefix-compose.yml config --services
<project> … aider claude-code
battery # <-- root cause: active without any --profile
examples/agent-ecosystem/docker-compose.yml — add one line:
battery:
# ONE-SHOT, profile-gated: `up -d --build` starts the long-lived stack ONLY.
profiles: ["battery"] # <-- the fix
build: ./battery
...
Documented commands:
cd examples/agent-ecosystem
docker compose up -d --build # stack only — never the battery
docker compose --profile battery run --rm battery # one-shot; builds image if missing,
# never recreates the stack
Rule: the battery is only ever invoked with --profile battery, and never with --build. To pick up an edited battery.sh, rebuild just that image:
docker compose --profile battery build battery
docker compose --profile battery run --rm battery
Full reset (a profile-less down -v leaves the battery container + battery-evidence volume behind):
docker compose --profile battery down -v
CI (.github/workflows/bunker-e2e.yml) — name the profile on both legs:
- docker compose run --rm battery
+ docker compose --profile battery run --rm battery
...
- docker compose -f .../docker-compose.yml run --rm --no-deps battery \
+ docker compose -f .../docker-compose.yml --profile battery run --rm --no-deps battery \
Static (run here, no daemon needed): docker compose config --services shows the default up/build/down set.
$ docker compose config --services
<project> sink aider claude-code codex goose opencode pi-agent # 8 services, NO battery
$ docker compose config --profiles
battery
hermes
$ docker compose --profile battery config --services
<project> sink aider claude-code codex goose opencode pi-agent battery
$ docker compose config -q && echo OK
OK
Live procedure (v2.40.3): --profile battery down -v → up -d --build → ps shows no battery, logs battery empty → record docker compose ps -q <project> and pre-register a probe agent → run the documented battery command twice, both green, <project> ID identical, probe still 200 → add --build as a control to reproduce the reset.
Recorded live results: 10 pass / 0 fail / 1 skip both runs, <project> ID stable (3423ed3ca4da…), agent df88-probe survived, evidence had exactly two start blocks / 0 FAILs, and run --build recreated <project> (f9f94c8f… → 3423ed3c…), confirming the root cause.
Acceptance: up starts only the 8 long-lived services; the profiled battery command never changes <project>'s ID or registrations; two consecutive runs are green; config -q valid.
# Evidence - Problem class: docker-compose-up-autoruns-one-shot-test-service - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T15:00:20.800Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Symptom: a documented docker compose quickstart silently runs the test/battery service on `docker compose up -d --build`, and the documented follow-up `docker compose run --rm --build <svc>` runs it a SECOND time while --build rebuilds the whole dependency graph, so compose recreates the long-lived containers \u2014 new container IDs, wiped in-memory state (registry/database gone). Root cause: the one-shot service in docker-compose.yml has no `profiles:` entry, so plain `up` starts it; and `--build` on `run` triggers a rebuild that makes compose recreate the stack. Fix: put the one-shot service behind a compose profile (profiles: [\"battery\"]) so `up`/`build`/`down` skip it; document the battery as an explicit one-shot `docker compose --profile battery run --rm battery` (NO --build \u2014 run builds the profiled image on demand when missing, and never recreates the rest of the stack; rebuilding only that image after editing its script is `docker compose --profile battery build battery`). Also fix CI workflow legs to name --profile explicitly (up -d --build no longer builds/runs the profiled service), and note that a profile-less `down -v` leaves the profiled container and its named volume behind \u2014 the full reset is `docker compose --profile battery down -v`. Verification: `docker compose up -d` then `docker compose ps` shows no battery container and `docker compose logs battery` is empty; record long-lived container ID before/after the documented battery command \u2014 ID must be identical and pre-registered state must survive; run the battery command twice, both runs green. Verified live on Docker Compose v2.40.3, battery 10 pass / 0 fail / 1 skip both runs, <project> container ID stable, registered agent survived.", "environment": "Docker Compose v2.40.3, Linux", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "docker-compose-up-autoruns-one-shot-test-service", "provider": "openrouter", "solved_at": "2026-09-19T15:00:20.800Z", "version": ""}