**Implementation Plan: Remove Stopped Containers** **Objective:** Eliminate the 10 stale stopped containers (and prevent future accumulation) by adding a dedicated `prune-stopped` command that reuses the existing removal pipeline (mark-for-removal + reconcile). --- ### Changes Required 1. **`devplacepy/services/containers/service.py`** – Add method `prune_stopped()`: - Query `store.all_instances()` for rows where `status == store.ST_STOPPED` and `desired_state == store.DESIRED_STOPPED`. - For each matching instance, call `api.mark_for_removal(uid)` (this sets status to `ST_REMOVING` and desired_state to `DESIRED_REMOVED`). - After all marks, call `self.run_once()` so the reconcile loop processes the removal (calling `backend.rm()` + `store.delete_instance()`). 2. **`devplacepy/cli/containers.py`** – Add subcommand `prune-stopped`: - Insert a new entry in the `argparse` subparser (`parser_cmd_containers`) after `prune` (or at the end). - The handler calls `ContainerService().prune_stopped()` asynchronously (using `asyncio.run()`). - Print a summary of how many instances were marked for removal. 3. **`devplacepy/templates/docs/services-containers.html`** – Update CLI reference: - Add `prune-stopped` to the list of subcommands (line 110 area). 4. **`tests/unit/services/containers.py`** – Add unit test for `prune_stopped()`: - Create a few fake instances with `ST_STOPPED`/`DESIRED_STOPPED`. - Call `service.prune_stopped()`. - Assert that `mark_for_removal` was called for each, and that `run_once` was called. --- ### Definition of Done - The `devplace containers prune-stopped` CLI command exists and correctly removes all stopped containers (Docker container + DB record) via the standard removal pipeline. - The code passes the project’s verification commands run from the repository root: ```bash pip install -e '.[dev]' -q && make test-unit pip install -q ruff && ruff check . ``` (If the environment has PEP 668 restrictions, use `--break-system-packages` or a virtual environment as appropriate.) - All existing tests continue to pass (no regressions). - The documentation template lists `prune-stopped` as an available CLI subcommand.