Skip to content

fix: discover orphan compose networks so wt prune can reclaim subnet slots#4

Merged
pkudinov merged 1 commit into
mainfrom
fix/network-orphan-detection
May 19, 2026
Merged

fix: discover orphan compose networks so wt prune can reclaim subnet slots#4
pkudinov merged 1 commit into
mainfrom
fix/network-orphan-detection

Conversation

@pkudinov

Copy link
Copy Markdown
Contributor

Summary

  • Label the compose default network with the same dev.tokenbooks.wt.* labels we already put on containers, so Docker carries the wt project identity on the network itself.
  • Extend listManagedDockerProjectsForRepo to scan docker network ls --filter label=…repo-root=… in addition to containers, so projects whose containers were reaped but whose network survives are still discoverable. wt prune already iterates this list, so the recovery path lights up automatically — no command-surface changes.

Why

wt new started failing with:

Error response from daemon: all predefined address pools have been fully subnetted
failed to create network wt-4-tokenbooks-<hash>_default

On the affected machine, docker network ls showed 24 wt-*-…_default networks but only one had any containers — the other 23 were holding subnet slots from Docker's default address pool with no way for wt prune to find them (its discovery starts from container labels, and Compose-applied labels don't carry the wt repo identity).

After this fix, wt prune finds and removes the orphans. The leak window remains — Ctrl+C during docker compose up, Docker Desktop reset, etc. can still strand a network — but recovery becomes a wt prune away instead of a manual docker network prune.

Test plan

  • Unit tests for the new behaviour (TDD-style, RED-then-GREEN):
    • buildDockerComposeConfig renders the labeled default network
    • listManagedDockerProjectsForRepo reports network-only orphans
    • It does not double-count projects that have both containers and a network
    • The network discovery uses the repo-root + managed label filters
  • Integration test (WT_RUN_DOCKER_TESTS=1) that exercises real Docker:
    1. ensureDockerServices starts redis
    2. docker rm -f the container to simulate a reap that leaves the network behind
    3. listManagedDockerProjectsForRepo reports the orphan with services: [], containerNames: []
    4. removeDockerServices cleans the network up
  • Full pre-existing suite still green (pnpm test, pnpm lint, pnpm build).

🤖 Generated with Claude Code

…Repo

Docker Compose only removes its project network during `compose down`.
When containers are reaped some other way — `docker rm -f`, Docker
Desktop reset, OS crash, or a `wt new` killed mid-flight before the
rollback path runs — the network survives but loses its only link to
the wt label set, which was previously only applied to containers.

Each leaked network still consumes a subnet slot from Docker's default
address pool. After ~30 leaks `wt new` fails with `all predefined
address pools have been fully subnetted` and `wt prune` cannot find the
projects to clean (its discovery starts from container labels).

Two changes:

1. `buildDockerComposeConfig` now emits a labeled `networks.default`
   carrying the same `dev.tokenbooks.wt.*` metadata as containers.
   Compose applies these on the real Docker network.

2. `listManagedDockerProjectsForRepo` scans `docker network ls` with
   the repo-root + managed filters in addition to the existing
   container scan, then merges by project name (no duplication when
   both containers and a network exist).

`wt prune` already iterates `listManagedDockerProjectsForRepo` results
and removes orphan projects, so the recovery path lights up
automatically — no command-surface changes.

Tests:
- Unit: network labels rendered, orphan discovery, dedup, filter shape
- Integration (WT_RUN_DOCKER_TESTS=1): real Compose run, force-remove
  the container, confirm `listManagedDockerProjectsForRepo` reports
  the survivor and `removeDockerServices` cleans it up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves wt prune’s ability to reclaim Docker subnet slots by ensuring Compose networks carry the same dev.tokenbooks.wt.* identity labels as containers, and by extending project discovery to include labeled networks (so projects remain discoverable even if their containers were removed).

Changes:

  • Add dev.tokenbooks.wt.* labels to the Compose default network generated by buildDockerComposeConfig.
  • Extend listManagedDockerProjectsForRepo to discover “network-only” orphan projects via docker network ls + docker network inspect.
  • Add unit + Docker integration tests covering network orphan discovery and non-duplication behavior; update README documentation for the new recovery path.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/core/docker-services.ts Labels the default Compose network and adds network-based discovery for managed Docker projects.
README.md Documents that wt prune now also recovers orphaned Docker networks that consume subnet slots.
__tests__/docker-services.spec.ts Adds unit tests for network labeling and network-only orphan discovery (including dedupe + filter assertions).
__tests__/docker-services.docker.spec.ts Adds an integration test that removes a container while leaving its network, verifying discovery + cleanup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/docker-services.ts
Comment thread src/core/docker-services.ts
@pkudinov pkudinov merged commit 10879db into main May 19, 2026
8 checks passed
@pkudinov pkudinov deleted the fix/network-orphan-detection branch May 19, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants