# AGENTS.md — Where Woof (resume brief) > The **source of truth is `where_woof.md`** (product plan + roadmap) and the **`openspec/` changes**. This file is a quick-resume overview for any agent or session picking the project back up. > > **Fuller docs: `docs/ARCHITECTURE.md` (how systems tie together + where things run), `docs/TOOLS.md` (toolchain), `docs/RESUME-HIGHLIGHTS.md` (resume-ready summary).** ## What this is Where Woof — a **return-tag platform** (not a tracker): pre-coded QR/NFC tags on items; a finder scans → sees return details → owner gets an SMS alert (with finder location when permitted). Branding: "Where Woof !" (werewolf wordplay). ## How we work (the pipeline) - **Master/worker**: the main pi session (cwd = repo root) orchestrates; subagents do chunks — `database` (schema/queries), `coder-pro` (Go code), `devops-pro` (infra/secrets), `code-analysis` (review). User reviews via plannotator's browser UI when it cooperates; otherwise inline approval. - Per change: **openspec** (propose → design/specs → tasks, `openspec validate`) → **plannotator** plan (`plans/.md`, submit → approve) → execute with `[DONE:n]` → run `/tmp` verify suites → `code-analysis` review → commit + push to Gitea. - This is a **learning exercise for the user** — explain decisions, don't just execute. ## Where we are (2026-08-07 EOD) - **Phase 1 `frontend-foundation` — DONE** (22/22) - **Phase 2 `scan-flow` — DONE** (19/19 + real SMS via SMSGlobal HTTP API) - **Phase 2.5 `scan-alert-hardening` — DONE** (12/12: different-finder re-alert + fingerprint 24h block) - **Phase 3 HTMX/Alpine polish — DONE** (+ tuxedo notes: photo upload, username/email display, favicon) - **Phase 4 `laravel-admin` — DONE** (Docker Laravel + Filament on .27:3030, shared Postgres, verified) - **Phase 7 `deploy-frontend` — LIVE on .13** (systemd user service :3020, Caddy blocks saved on .35) - ⏳ **Outstanding (user, AM)**: where-woof.com DNS/redirect — A record still at InMotion (173.231.229.92); lab domains use a redirect mechanism (NOT a plain A record — user correction, investigate how both are configured); may involve router/NAT settings - Admin (`admin.where-woof.com` → .13:3030) is Phase 8 — nothing runs on 3030 yet - **Remaining**: Phase 5 billing, Phase 6 photos, Phase 8 admin deploy, Phase 9 hardening ## Key locations | What | Where | |---|---| | Product plan + roadmap (source of truth) | `where_woof.md` | | OpenSpec specs & changes | `openspec/` (`frontend-foundation` ✅, `scan-flow` ✅) | | Plannotator plans | `plans/frontend-foundation.md`, `plans/scan-flow.md` | | Go front-end | `frontend/` (stdlib `net/http`, `internal/{db,auth,handlers,sms}`) | | Schema | `db/schema.sql` (canonical until Laravel owns migrations) | | Build/run targets | `Makefile` — `db-up`, `seed`, `run`, `build`, `generate`, `psql` | | Dev DB | `wherewoof-db` Postgres 16 container on **.13:5434** (user/db `wherewoof`; compose + `.env` in `/home/sam/Docker/Containers/wherewoof-db/` on .13) | | Run locally | `make db-up && make seed && make run` → `http://localhost:3020` (SMS in **log mode** until creds set) | | Live frontend | **.13:3020** systemd user service `where-woof` (`/var/www/where-woof`, env `~/.config/where-woof.env`); `where-woof.home.lab` works via .35 Caddy | | Admin (dev) | **.27:3030** Docker Laravel (`admin/`, Filament) — `cd admin && docker compose up -d`; live deploy = Phase 8 | | Verify suites | `/tmp/verify.sh` (Phase 1, 22), `/tmp/verify2.sh` (Phase 2, 19), `/tmp/verify3.sh` (hardening, 12) — ephemeral, they reset the DB (incl. admin user — re-seed via `php artisan wherewoof:admin`) | | Prod target | `.13:/var/www/where-woof` — **frontend LIVE**; admin not yet deployed; Caddy on .35; `where-woof.com` DNS at InMotion (outstanding) | Gitea | `sam/where_woof` — `ssh://git@gitea.lab.audasmedia.com.au:2222/sam/where_woof.git` | ## Tools - `openspec` 1.3.1 (spec-driven dev), `plannotator` (plan + browser review), pi subagents (master/worker), `sqlc` 1.31.1 at `~/go/bin` (not on PATH — `make generate` handles it), `pgx` v5, `gorilla/sessions`, `bcrypt` - **Secrets are env-only, never committed**: `DATABASE_URL`, `SESSION_SECRET`; SMS via **HTTP API** `SMS_USER`/`SMS_PASSWORD`/`SMS_FROM` (proven live 2026-08-07) with REST key/secret (`SMS_API_KEY`/`SMS_API_SECRET`) as unverified fallback - SMS is a swappable `internal/sms.Sender` — `LogSender` (default) / `smsglobal.Client` (real) ## Gotchas worth remembering - `.pi/` is gitignored (agent memory + extension tooling are machine-local) - NixOS box: **no gcc** (`CGO_ENABLED=0` set via `go env -w`), `make` via `nix profile`, no `psql` (use `make psql` → docker exec on .13) - `html/template` escapes `+` as `+` in hrefs (`tel:`/`sms:` links still work in a browser) - `pkill -f` can match your own shell — use `pkill -x where-woof` - Reset the DB between the two verify suites (both bind `TEST000001`) - Postgres identity columns advance on failed inserts (duplicate-email test consumes an id) ## KNOWN-GOOD STATE (2026-08-10) — read before testing - **User photo upload**: inside the MAIN edit form (`frontend/templates/tag-edit.html`) — `` + Save; no JS needed. Live preview via FileReader (`#photo-preview`). `photo_url` input is `type="text"` (never `type="url"` — relative `/photos/...` paths fail URL validation). EditTag handler stores the file to MinIO when present, else uses the text URL. Do NOT use `-F "phone=%2B..."` in curl tests — curl `-F` does NOT decode `%XX`; it stores the literal `%2B`. Tag 6 data: phone +61423274487, address "9 Toora St Ivanhoe 3079", photo /photos/tags/32.jpg. - **Admin photo**: TagResource photo_url = TextInput (+ ImageColumn). The edit page header/inline "Upload photo" action modal FileUpload stores to MinIO (`/photos/{key}`) — NO `->image()` (rejects HEIC/phone formats → "failed to upload"). **Never use Filament FileUpload bound to a string column directly** (getUploadedFiles foreach over string = 500). Relative `Filament\...` namespaces in resource files break the whole panel (leading `\` required). - **Admin upload PHP limits**: raised via compose `command: php -d upload_max_filesize=12M -d post_max_size=14M artisan serve ...` (check `docker inspect` Cmd, not `docker exec php -r`). - **Logins**: admin@where-woof.com / AdminPass123! · owner@where-woof.com / OwnerPass123! · seed-admin writes `$2y$` bcrypt (Go prefix rewrite) so Laravel login works. - **Deploy**: explicit `rsync --checksum ` per file (dir rsync flaky due to clock skew); restart `systemctl --user restart where-woof` + `docker compose restart` in admin dir. Option (b): no lsyncd. - Test suites wipe the live DB (users/tags) — re-seed via `make db-up` (admin auto-seeded) + `make seed` + `make seed-registry`.