---
id: T02
parent: S03
milestone: M007
provides: []
requires: []
affects: []
key_files: ["docker-compose.yml", ".gsd/milestones/M007/slices/S03/tasks/T02-SUMMARY.md"]
key_decisions: ["Used python os.kill(1,0) healthcheck instead of pgrep since procps unavailable in slim Python image", "SCP'd files to ub01 directly instead of git push to avoid outward-facing GitHub action"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "All slice-level checks pass (py_compile, watchdog grep, PollingObserver grep, processed/failed grep). Container running healthy on ub01. End-to-end valid transcript ingest verified (POST 200, file in processed/). Error path verified (invalid JSON in failed/ with .error sidecar)."
completed_at: 2026-03-30T19:24:18.070Z
blocker_discovered: false
---

# T02: Added chrysopedia-watcher service to Docker Compose, deployed and verified end-to-end file-drop auto-ingest on ub01

> Added chrysopedia-watcher service to Docker Compose, deployed and verified end-to-end file-drop auto-ingest on ub01

## What Happened
---
id: T02
parent: S03
milestone: M007
key_files:
  - docker-compose.yml
  - .gsd/milestones/M007/slices/S03/tasks/T02-SUMMARY.md
key_decisions:
  - Used python os.kill(1,0) healthcheck instead of pgrep since procps unavailable in slim Python image
  - SCP'd files to ub01 directly instead of git push to avoid outward-facing GitHub action
duration: ""
verification_result: passed
completed_at: 2026-03-30T19:24:18.070Z
blocker_discovered: false
---

# T02: Added chrysopedia-watcher service to Docker Compose, deployed and verified end-to-end file-drop auto-ingest on ub01

**Added chrysopedia-watcher service to Docker Compose, deployed and verified end-to-end file-drop auto-ingest on ub01**

## What Happened

Added the chrysopedia-watcher service to docker-compose.yml reusing Dockerfile.api with a python watcher.py command override. Bind-mounts /vmPool/r/services/chrysopedia_watch to /watch, depends on chrysopedia-api healthy. Fixed healthcheck from pgrep (unavailable in slim image) to python-based PID 1 check. Deployed to ub01, verified end-to-end: valid JSON file auto-ingested (HTTP 200, moved to processed/), invalid JSON moved to failed/ with .error sidecar.

## Verification

All slice-level checks pass (py_compile, watchdog grep, PollingObserver grep, processed/failed grep). Container running healthy on ub01. End-to-end valid transcript ingest verified (POST 200, file in processed/). Error path verified (invalid JSON in failed/ with .error sidecar).

## Verification Evidence

| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `python -m py_compile backend/watcher.py` | 0 | ✅ pass | 200ms |
| 2 | `grep -q 'watchdog' backend/requirements.txt` | 0 | ✅ pass | 50ms |
| 3 | `grep -q 'PollingObserver' backend/watcher.py` | 0 | ✅ pass | 50ms |
| 4 | `grep -q 'processed' backend/watcher.py && grep -q 'failed' backend/watcher.py` | 0 | ✅ pass | 50ms |
| 5 | `ssh ub01 docker ps --filter name=chrysopedia-watcher (healthy)` | 0 | ✅ pass | 1000ms |
| 6 | `E2E valid JSON → processed/` | 0 | ✅ pass | 15000ms |
| 7 | `E2E invalid JSON → failed/ + .error sidecar` | 0 | ✅ pass | 15000ms |


## Deviations

Changed healthcheck from pgrep -f watcher.py to python -c "import os; os.kill(1, 0)" because procps is not available in the slim Python Docker image.

## Known Issues

None.

## Files Created/Modified

- `docker-compose.yml`
- `.gsd/milestones/M007/slices/S03/tasks/T02-SUMMARY.md`


## Deviations
Changed healthcheck from pgrep -f watcher.py to python -c "import os; os.kill(1, 0)" because procps is not available in the slim Python Docker image.

## Known Issues
None.
