| 1 | # DropHawk - Phase 1 build status |
| 2 | |
| 3 | **Built:** 2026-06-22 · **Scope:** Phase 1 vertical slice (Ticketmaster → ntfy → dashboard), per `CLAUDE.md` / `SPEC.md` §14 / `CONTRACTS.md`. |
| 4 | **Source of truth:** this repo, `/shared/projects/drophawk` (RAG-indexed + PBS-backed). Original GLM seed lives at CT247 `/root/drophawk`. |
| 5 | |
| 6 | ## What's implemented |
| 7 | |
| 8 | | Layer | State | Notes | |
| 9 | |---|---|---| |
| 10 | | **Backend** (Rust/Axum/sqlx) | ✅ code complete | Ticketmaster-only adapter. 12 migrations (DDL verbatim from §G). Every §B.3 endpoint + §B.1 envelope. SSE `/api/drops/stream` with `Last-Event-ID` replay + 15s heartbeat. §D `content_hash` with golden-vector test (`tests/hash_golden.rs`). §E classification. §I ntfy publisher + watchlist/dedupe/quiet-hours filter. §C adaptive poller + circuit breaker. `#![forbid(unsafe_code)]`. | |
| 11 | | **Frontend** (Next.js 16 / Tailwind v4 / Framer Motion) | ✅ code complete | Honors binding OKLCH/teal tokens (§H). Live SSE feed, spring drop cards + teal flash, count-up KPIs, ⌘K palette, collapsible sidebar, watchlist CRUD, source health grid, settings + test push. WCAG 2.2 AA intent. | |
| 12 | | **Infra** (compose/Caddy/ntfy/observability) | ✅ code complete | `docker-compose.yml` (+ prod overrides), `Makefile` (12 targets), SSE-safe Caddy, Prometheus/Grafana/Loki, backup + healthcheck scripts. | |
| 13 | | Python scrapers (FIFA/AXS/…) | ⛔ Phase 3 | Not in Phase 1 scope. | |
| 14 | | Meilisearch / explore / heatmap | ⛔ Phase 2 | `Explore` nav item shows "soon". | |
| 15 | |
| 16 | ## Cross-stack alignment (verified statically) |
| 17 | - Backend binds `0.0.0.0:8080`; Caddy proxies `/api/*` → `backend:8080` with `flush_interval -1`; frontend dev rewrites `/api` → `backend:8080`. |
| 18 | - SSE path `/api/drops/stream` + event names (`drop.new|status|price|inventory|announcement`, `source.health`) match the frontend client. |
| 19 | - Response envelope (`data`/`meta`/`error.{code,message,request_id}`) matches the frontend `ApiError` parser. |
| 20 | |
| 21 | ## Not yet done (requires a real host + operator input) |
| 22 | Builds were **not** run here - `/shared/projects` is noexec CIFS with no Rust/Node toolchain. Code is compile-intentional. To bring it up: |
| 23 | |
| 24 | 1. **On a Docker host** (e.g. CT120): `cp -r /shared/projects/drophawk /opt/drophawk && cd /opt/drophawk`. |
| 25 | 2. `cp .env.example .env` and fill in: |
| 26 | - `TICKETMASTER_API_KEY` - free at developer.ticketmaster.com (operator action). |
| 27 | - ntfy topic/token (default topic `drophawk-drops`). |
| 28 | - Postgres/Redis/NATS creds (any strong values). |
| 29 | 3. `make up` → `make migrate` → open the dashboard via Caddy (`http://<host>/`). |
| 30 | 4. On your phone: install **ntfy**, subscribe to the `drophawk-drops` topic. |
| 31 | 5. Add **"Taylor Swift"** in Watchlist → when a TM event flips on-sale you get a push + it streams into the live feed. That's the Phase 1 success criterion (CLAUDE.md). |
| 32 | |
| 33 | ## Key decisions / deviations (full list in commit + agent reports) |
| 34 | - Backend added 4 deps the contracts require but §A.1 omitted: `regex` (watchlist validation, §B.3), `rand` (jitter, §C), `tokio-stream` (SSE bridge). Flagged, behavior-only. |
| 35 | - Migration `set_updated_at` fn reordered before `sources` (sqlx runs in filename order); DDL bytes unchanged. |
| 36 | - Error envelope follows §B.1 (includes `request_id`) where it conflicted with §K. |
| 37 | - SSE heartbeat uses Axum `KeepAlive` (`: heartbeat`) at 15s; the live-epoch suffix is omitted (Axum limitation). |
| 38 | - Infra uses `timescale/timescaledb-ha:pg16` instead of building Timescale into `postgres:16.10` - cleanest path; swap to a custom Postgres Dockerfile if exact base is required. |
| 39 | - Frontend uses a styled native `<select>` for the watchlist form (full a11y) rather than Radix Select; reserved Radix Select for richer menus. |
| 40 | |
| 41 | ## Do NOT start Phase 2 until the operator verifies Phase 1 end-to-end (CLAUDE.md hard rule #1). |