---
estimated_steps: 46
estimated_files: 1
skills_used: []
---

# T02: Docker Compose integration and end-to-end verification on ub01

Add the chrysopedia-watcher service to docker-compose.yml, create the watch folder on ub01, build and deploy, and verify end-to-end file-drop ingestion.

**Slice:** S03 — Transcript Folder Watcher — Auto-Ingest Service
**Milestone:** M007

## Description

Wire the watcher script into the Docker Compose stack as a new service. The watcher reuses the existing `Dockerfile.api` image (same Python + deps) but runs `python watcher.py` instead of uvicorn. The watch folder on ub01 is `/vmPool/r/services/chrysopedia_watch/` bind-mounted to `/watch` in the container.

This task deploys to ub01 and verifies end-to-end: drop a transcript JSON → watcher picks it up → POSTs to API → video record created.

**Important context for executor:** The canonical codebase lives on ub01 at `/vmPool/r/repos/xpltdco/chrysopedia`. You must SSH to ub01 to edit docker-compose.yml, build, and test. The watcher.py from T01 needs to be committed and available in the repo on ub01.

## Failure Modes

| Dependency | On error | On timeout | On malformed response |
|------------|----------|-----------|----------------------|
| Docker build | Fix Dockerfile or requirements, rebuild | N/A | N/A |
| API healthcheck | Watcher waits (depends_on condition: service_healthy) | Start timeout → container restarts | N/A |

## Steps

1. SSH to ub01 and add the `chrysopedia-watcher` service to `docker-compose.yml`:
   - `build: context: . dockerfile: docker/Dockerfile.api` (reuse existing image)
   - `container_name: chrysopedia-watcher`
   - `restart: unless-stopped`
   - `command: ["python", "watcher.py"]`
   - `environment: WATCHER_API_URL: http://chrysopedia-api:8000/api/v1/ingest, WATCH_FOLDER: /watch`
   - `volumes: /vmPool/r/services/chrysopedia_watch:/watch`
   - `depends_on: chrysopedia-api: condition: service_healthy`
   - `networks: chrysopedia`
   - `healthcheck: test: ["CMD-SHELL", "pgrep -f watcher.py || exit 1"] interval: 30s timeout: 5s retries: 3`
2. Create the watch folder on ub01: `mkdir -p /vmPool/r/services/chrysopedia_watch`
3. Ensure T01's `backend/watcher.py` and updated `backend/requirements.txt` are committed and pushed to the repo on ub01 (git pull if needed)
4. Build and deploy: `cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose build chrysopedia-watcher && docker compose up -d chrysopedia-watcher`
5. Verify watcher is running: `docker exec chrysopedia-watcher pgrep -f watcher.py`
6. End-to-end test: Copy a known-good transcript JSON to `/vmPool/r/services/chrysopedia_watch/test_ingest.json` → check watcher logs for pickup and 200 → verify file moved to `processed/`
7. Error test: Create an invalid JSON file in the watch folder → verify it moves to `failed/` with `.error` sidecar

## Must-Haves

- [x] chrysopedia-watcher service defined in docker-compose.yml
- [x] Watch folder bind mount configured
- [x] Watcher container starts and stays healthy
- [x] End-to-end file-drop ingestion verified
- [x] Failed file handling verified

## Verification

- `ssh ub01 "docker ps --filter name=chrysopedia-watcher --format '{{.Status}}'"` shows healthy/running
- `ssh ub01 "docker logs chrysopedia-watcher 2>&1 | tail -5"` shows watcher startup message
- `ssh ub01 "ls /vmPool/r/services/chrysopedia_watch/processed/"` contains the test file
- `ssh ub01 "ls /vmPool/r/services/chrysopedia_watch/failed/"` contains the invalid test file + `.error` sidecar

## Inputs

- `backend/watcher.py` — watcher script from T01
- `backend/requirements.txt` — updated with watchdog dependency from T01
- `docker-compose.yml` — existing compose file to modify

## Expected Output

- `docker-compose.yml` — updated with chrysopedia-watcher service definition

## Inputs

- `backend/watcher.py`
- `backend/requirements.txt`
- `docker-compose.yml`

## Expected Output

- `docker-compose.yml`

## Verification

ssh ub01 "docker ps --filter name=chrysopedia-watcher --format '{{.Status}}'" | grep -qi 'healthy\|up'
