## Implementation Plan ### 1. Reliable auto-seed of daily backup schedule **Problem:** `seed_default_schedule()` is called in `BackupService.__init__`, which runs at service registration. If the `backup_schedules` table doesn’t exist yet (race with DB migration), the seed fails silently and is never retried. **Change:** - In `devplacepy/services/backup/service.py`, move the `store.seed_default_schedule()` call from `__init__` (line ~38) into the `run_once()` method (the main loop tick). - Wrap it in a guard that checks if any schedule already exists (using `store.get_schedules()`) – skip if one or more schedules are present. - Keep the existing `try/except` logging so failures are non-fatal. ```python # Inside BackupService.run_once(), early in the method: if not self.store.get_schedules(): try: self.store.seed_default_schedule() except Exception: self.logger.warning(...) ``` ### 2. Age-based eviction of stopped/crashed container instances **Problem:** `_cleanup_stale_workspaces()` only removes orphaned workspace directories (no matching DB instance). It does not remove container instances that are in `ST_STOPPED` or `ST_CRASHED` state for longer than a threshold (e.g., 7 days). These accumulate disk via container layers and workspace artifacts. **Change:** - In `devplacepy/services/container_ops.py`, add a new method `evict_stale_instances(max_age_days: int = 7)`. - Query all container instances whose `status` is in `[ST_STOPPED, ST_CRASHED]` and whose `updated_at` is older than `max_age_days`. - For each, call the existing `remove_instance(instance_id)` flow (which removes Docker container, workspace dir, etc.). - Call this method from `BackupService.run_once()` (or from the main container maintenance loop if one exists) – add it after the existing stale workspace cleanup. **Where to call:** Inside `run_once()` of `BackupService` (or its `_cleanup_stale_workspaces` – rename to `_cleanup_old_instances`). Since the backup service already runs periodic tasks, it’s a natural place. ### 3. Ensure virtual environment isolation for test/lint commands **Problem:** Earlier attempts failed because `pip install` hit PEP 668 (system package conflict). Must use a venv. **Fix:** At the start of execution, create a venv if not present, activate it, then run the test/lint commands inside it. ```bash python3 -m venv .venv source .venv/bin/activate pip install -e '.[dev]' -q # then run test or lint commands ``` This does not require changing repository code, only the execution environment. Include this in the execution shell. --- ### Definition of Done - [ ] `devplacepy/services/backup/service.py` modified: `seed_default_schedule()` moved to `run_once()` with a guard to avoid double seeding. - [ ] `devplacepy/services/container_ops.py` modified: new method `evict_stale_instances(max_age_days=7)` that removes stopped/crashed instances older than threshold. - [ ] `devplacepy/services/backup/service.py` modified: call `evict_stale_instances()` from `run_once()` (or from the same cleanup function). - [ ] All changes are committed on a branch that rebases cleanly onto the target base branch (no merge conflicts). - [ ] Verification: activate a fresh virtual environment and run: - `pip install -e '.[dev]' -q && make test-unit` → exit code 0, all tests pass. - `pip install -q ruff && ruff check .` → exit code 0, no lint errors. - [ ] The live instance (after deployment) will *automatically* have a daily backup schedule seeded on first service tick, and stopped/crashed containers older than 7 days will be cleaned up, reducing disk pressure.