Phase 1b of the AWS host design (docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md, "Host abstraction"). Refactor only; behaviour should not change. It finishes phase 1: apart from base-image migration, nothing outside host/ and docker/ knows the container runs on Docker.
What changed
Lifecycle behind host::lifecycle::ProjectHost (one Docker variant). It owns ensure_started, find_current, stop, remove, discard_project_state and remove_legacy_leftovers.
ensure_started is the old find → snapshot-and-recreate → create → start block from start_project_container_locked, moved verbatim with its comments.
Start, stop, Reset, remove and reconcile keep only the host-neutral steps: validation, status, sessions, auth bridge and syncs.
Exit watcher.container_watch.rs no longer uses bollard. Docker's die stream, ping and the positive-exit inspect moved to docker/events.rs and are reached through host::watch (ExitSource, ContainerTarget::has_exited). An AWS host can feed it by polling.
No more bare container ids. The auth bridge, browser view (including proxy and popout), file viewer, marketplace sync and the file/inspect commands now take a ContainerTarget. The started target flows to the auth bridge, marketplace sync and Bedrock credential sync.
Helpers moved.create_container_for_project and default_docker_socket moved into docker/; migration used both, and its duplicate socket helper is gone.
New boundary test. Non-test code outside host/ and docker/ may not call lifecycle functions or build ContainerTarget::docker(id). Migration is the single, documented exception. I checked with a planted probe file that it flags real calls and ignores comments and test modules.
CLAUDE.md documents the lifecycle and watch modules.
Verification
cargo test --lib: 912 passed, 0 failed.
Live tests against Docker 29.9.3:
New ignored test host::lifecycle::tests::a_project_container_round_trips_through_the_host takes a throwaway project (fresh UUID) through create, start, exec as claude, stop, has_exited, restart on the same container, remove and discard. It passes, the progress messages match, and the daemon's container and volume counts are unchanged afterwards.
The existing live watcher test and the file-commands container-rules test also pass.
Independent review with git access, comparing against 3ec100e: no runtime behaviour changes. It covered start order, which errors abort and which are ignored, progress text, the Reset/remove reports, stop, reconcile, watcher semantics and container-id ordering.
One theoretical difference: the watcher now counts only real die reports as a productive connection for backoff. The old code counted any event. Docker's filter means only die events arrive, so this can't happen in practice.
Not checked: I didn't open the app's UI by hand.
Next
Phase 2: the in-container triple-c-agent and an SSM port-forward client, which an AWS ContainerTarget variant needs.
Phase 1b of the AWS host design (`docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md`, "Host abstraction"). **Refactor only; behaviour should not change.** It finishes phase 1: apart from base-image migration, nothing outside `host/` and `docker/` knows the container runs on Docker.
## What changed
- **Lifecycle behind `host::lifecycle::ProjectHost`** (one Docker variant). It owns `ensure_started`, `find_current`, `stop`, `remove`, `discard_project_state` and `remove_legacy_leftovers`.
- `ensure_started` is the old find → snapshot-and-recreate → create → start block from `start_project_container_locked`, moved verbatim with its comments.
- Start, stop, Reset, remove and reconcile keep only the host-neutral steps: validation, status, sessions, auth bridge and syncs.
- **Exit watcher.** `container_watch.rs` no longer uses bollard. Docker's `die` stream, ping and the positive-exit inspect moved to `docker/events.rs` and are reached through `host::watch` (`ExitSource`, `ContainerTarget::has_exited`). An AWS host can feed it by polling.
- **No more bare container ids.** The auth bridge, browser view (including proxy and popout), file viewer, marketplace sync and the file/inspect commands now take a `ContainerTarget`. The started target flows to the auth bridge, marketplace sync and Bedrock credential sync.
- **Helpers moved.** `create_container_for_project` and `default_docker_socket` moved into `docker/`; migration used both, and its duplicate socket helper is gone.
- **New boundary test.** Non-test code outside `host/` and `docker/` may not call lifecycle functions or build `ContainerTarget::docker(id)`. Migration is the single, documented exception. I checked with a planted probe file that it flags real calls and ignores comments and test modules.
- **CLAUDE.md** documents the lifecycle and watch modules.
## Verification
- `cargo test --lib`: 912 passed, 0 failed.
- **Live tests against Docker 29.9.3:**
- New ignored test `host::lifecycle::tests::a_project_container_round_trips_through_the_host` takes a throwaway project (fresh UUID) through create, start, exec as `claude`, stop, `has_exited`, restart on the same container, remove and discard. It passes, the progress messages match, and the daemon's container and volume counts are unchanged afterwards.
- The existing live watcher test and the file-commands container-rules test also pass.
- **Independent review with git access**, comparing against 3ec100e: no runtime behaviour changes. It covered start order, which errors abort and which are ignored, progress text, the Reset/remove reports, stop, reconcile, watcher semantics and container-id ordering.
- **One theoretical difference:** the watcher now counts only real `die` reports as a productive connection for backoff. The old code counted any event. Docker's filter means only `die` events arrive, so this can't happen in practice.
- **Not checked:** I didn't open the app's UI by hand.
## Next
Phase 2: the in-container `triple-c-agent` and an SSM port-forward client, which an AWS `ContainerTarget` variant needs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
container_watch keeps all of its logic (sweep, compare-and-set settle,
backoff, cancellation) but no longer touches bollard: reachability, the
death stream and the positive-exit check come from host::watch::ExitSource
and ContainerTarget::has_exited, implemented for Docker in docker/events.rs.
An AWS host can supply deaths by polling. container_watch.rs leaves the
boundary test's allowlist.
Verified with the live end-to-end watcher test against a real daemon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Functions that took a bare container id and built
`ContainerTarget::docker(id)` internally now take a target, and their
callers resolve one from the project (`ContainerTarget::for_project`) or
carry the one they already hold. Long-lived holders (auth bridge poller,
browser-view proxy/session, file viewer commands) store the target.
Collapses the string/target wrapper pairs: `detect::detect` now takes a
target (`detect_in` removed) and `tunnel_connection_with_prelude` takes a
target (`tunnel_into_with_prelude` removed).
No behaviour change. Two temporary Docker constructions remain on the
container-start path, marked "phase 1b": the auth bridge start in
`start_project_container_locked` and `marketplace::spawn_project_sync`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 1b of the AWS host work. ProjectHost (one Docker variant) owns
ensure_started (find → snapshot-and-recreate → create → start, moved
verbatim from start_project_container_locked), find_current, stop, remove,
discard_project_state and remove_legacy_leftovers. Start, stop, Reset,
remove and reconcile keep only host-neutral steps and ask the host for the
rest; the started ContainerTarget now flows to the auth bridge, marketplace
sync and Bedrock credential sync.
- create_container_for_project and default_docker_socket move to docker/
(migration used both; its duplicate socket helper is gone).
- sync_bedrock_credentials and marketplace::spawn_project_sync take a
ContainerTarget.
- New boundary test: no lifecycle calls or ContainerTarget::docker(id) in
non-test code outside host/ and docker/ (migration excepted).
- Ignored live test round-trips a throwaway project container through the
host; passes against Docker 29.9.3 and leaves nothing behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Phase 1b of the AWS host design (
docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md, "Host abstraction"). Refactor only; behaviour should not change. It finishes phase 1: apart from base-image migration, nothing outsidehost/anddocker/knows the container runs on Docker.What changed
host::lifecycle::ProjectHost(one Docker variant). It ownsensure_started,find_current,stop,remove,discard_project_stateandremove_legacy_leftovers.ensure_startedis the old find → snapshot-and-recreate → create → start block fromstart_project_container_locked, moved verbatim with its comments.container_watch.rsno longer uses bollard. Docker'sdiestream, ping and the positive-exit inspect moved todocker/events.rsand are reached throughhost::watch(ExitSource,ContainerTarget::has_exited). An AWS host can feed it by polling.ContainerTarget. The started target flows to the auth bridge, marketplace sync and Bedrock credential sync.create_container_for_projectanddefault_docker_socketmoved intodocker/; migration used both, and its duplicate socket helper is gone.host/anddocker/may not call lifecycle functions or buildContainerTarget::docker(id). Migration is the single, documented exception. I checked with a planted probe file that it flags real calls and ignores comments and test modules.Verification
cargo test --lib: 912 passed, 0 failed.host::lifecycle::tests::a_project_container_round_trips_through_the_hosttakes a throwaway project (fresh UUID) through create, start, exec asclaude, stop,has_exited, restart on the same container, remove and discard. It passes, the progress messages match, and the daemon's container and volume counts are unchanged afterwards.3ec100e: no runtime behaviour changes. It covered start order, which errors abort and which are ignored, progress text, the Reset/remove reports, stop, reconcile, watcher semantics and container-id ordering.diereports as a productive connection for backoff. The old code counted any event. Docker's filter means onlydieevents arrive, so this can't happen in practice.Next
Phase 2: the in-container
triple-c-agentand an SSM port-forward client, which an AWSContainerTargetvariant needs.🤖 Generated with Claude Code