# CLAUDE.md — Phase 1: First Tracker Scrape, First View

## Scope of This File

This file instructs you (Claude Code) to implement **Phase 1 only** of the Seedbox Command Center.

**Do not implement phases 2 through 15.** A separate CLAUDE.md will be written for each subsequent phase. Stay in scope.

## Authoritative Specification

`PROJECT.md` in this repository is the master architecture document. When any question arises about design intent — what a feature should do, how a subsystem should behave, what technology to use — consult `PROJECT.md`. Do not invent design decisions not present there.

Before starting Phase 1, also read:
- `PROGRESS.md` to confirm Phase 0 actually shipped what its CLAUDE.md promised.
- `DECISIONS.md` to absorb the architectural decisions Phase 0 already made — those are binding.
- The Phase 0 source under `internal/` so that Phase 1 reuses existing primitives (logger, secrets, config, db, events) rather than re-implementing them.

If you encounter a genuine ambiguity that `PROJECT.md` does not resolve, document the question and your chosen interpretation in `DECISIONS.md` and proceed. Do not stop to ask the operator unless the ambiguity blocks all forward progress.

## Phase 1 Mission

End-to-end value, narrowly scoped: the operator can register one private tracker, the system periodically scrapes that tracker's profile page, and the mobile-installable PWA shows a single tracker's current ratio with a history graph.

Phase 1 introduces the architecture pieces every later phase will lean on: the integration adapter pattern, the scrape hygiene primitive, the periodic-job scheduler, and the snapshot writer that hits both SQLite (transactional state) and DuckDB (analytical store).

The integration is real. No mocks. One tracker, working against a live tracker site.

## Deliverables Checklist

Phase 1 is complete when every item below is true:

- [ ] `internal/scrape` package exists with a `Hygiene` primitive that randomizes timing within an operator-configured window, honors `Retry-After`, applies exponential backoff with jitter on consecutive failures, and persists per-tracker cookies via the Phase 0 `internal/secrets` store.
- [ ] `internal/integrations/trackers` package defines a `TrackerAdapter` interface (`FetchRatio`, `FetchUserStats`, `Name`, `Health`) and ships exactly one concrete implementation. The choice of "which tracker" is operator-configurable but the codebase ships one. Document the choice in `DECISIONS.md`.
- [ ] Scrape failures classify into network / authentication / structural categories per `PROJECT.md §7.5` and `§8.9`. Each category writes a typed event to `system_events` via the Phase 0 `EventRecorder`.
- [ ] SQLite migration `002_phase1_trackers.sql` adds the `trackers`, `ratio_snapshots`, `torrents`, `torrent_trackers` tables exactly as in `PROJECT.md` Appendix A. The `ratio_snapshots` row is additionally written to DuckDB's `ratio_snapshots` table for analytical queries.
- [ ] A periodic scheduler (`internal/scrape/scheduler`) runs each enabled tracker's scrape on its configured interval with hygiene applied. On boot, schedule entries are loaded from the `trackers` table. The scheduler integrates with the Phase 0 graceful-shutdown context.
- [ ] Tracker definitions are operator-managed via YAML in the config-as-code directory (`config/trackers.yaml`). The loader validates them against a typed schema; invalid files are rejected and the previous valid config remains in effect, matching the Phase 0 pattern.
- [ ] HTTP API endpoints (under `/api/trackers`):
    - `GET /api/trackers` — list trackers.
    - `GET /api/trackers/:id` — one tracker's current state.
    - `GET /api/trackers/:id/snapshots?range=24h|7d|30d|all` — ratio history.
    - `POST /api/trackers/:id/refresh` — operator-triggered re-scrape (rate-limited).
- [ ] Frontend: a `/trackers/:id` route renders the tracker's current real-ratio and displayed-ratio cards plus a SVG line chart of ratio over the selected time range. The dashboard's root path (`/`) now lists all configured trackers with last-scrape recency indicators. The PWA install prompt is wired (manual `beforeinstallprompt` capture; tap-to-install button on first visit when running on iOS).
- [ ] At least one tracker is fully integrated end-to-end against the live site: cookies stored, scrape runs on its configured cadence, snapshots accumulate in SQLite and DuckDB, the UI reflects what the tracker site shows when both are open side by side.
- [ ] Every new package has at least one meaningful test. Adapter tests use `httptest.Server`; the hygiene primitive is tested for backoff timing, Retry-After honoring, and the failure classification rules.
- [ ] `PROGRESS.md` updated with Phase 1 completion summary, including operator verification steps and any blockers.
- [ ] `DECISIONS.md` records the chosen first tracker, the chosen HTML-parsing approach (struct-based, golang.org/x/net/html, goquery — your call), and any deviations.

## Phase 1 Database Scope

Add the following tables in `migrations/002_phase1_trackers.sql`, exactly as in `PROJECT.md` Appendix A:

- `trackers`
- `ratio_snapshots` (SQLite copy; the DuckDB copy was created in Phase 0)
- `torrents`
- `torrent_trackers`

The other Appendix A tables remain out of scope; later phases add them. Do not pre-create empty tables for future phases. The migration runner already supports additive migrations — use it.

## Repository Layout

Add the following directories. Do **not** create any directory not listed here.

```
internal/
  scrape/                       # hygiene primitive, scheduler, failure classifier
    hygiene.go
    scheduler.go
    classifier.go
    *_test.go
  integrations/
    trackers/                   # TrackerAdapter interface, one concrete impl
      adapter.go                # the interface
      <chosen>.go               # the concrete adapter
      *_test.go
config/
  trackers.yaml                 # operator-managed declarative tracker list
```

Files added under existing packages:
- `internal/server/routes_trackers.go` — handlers for the new `/api/trackers/*` endpoints.
- `internal/db/snapshots.go` — helpers for dual-write to SQLite and DuckDB.

Frontend additions:
- `web/src/pages/Trackers.tsx`
- `web/src/pages/TrackerDetail.tsx`
- `web/src/components/RatioChart.tsx`
- `web/src/lib/api.ts` — typed fetch wrappers around `/api/trackers/*` (you'll grow this file every phase).
- `web/src/lib/pwa.ts` — `beforeinstallprompt` capture and install button.

Subsequent phases will add `internal/auth`, `internal/eventbus`, `internal/integrations/qbit`, etc. Do not create those directories yet.

## Working Rules

**Use Phase 0's primitives.** Do not re-implement the logger, secrets store, config loader, db handles, or event recorder. If something is missing from those packages, extend them with a focused commit and explain why in `DECISIONS.md`.

**Hygiene is non-negotiable.** Every outbound HTTP request to a tracker site MUST pass through the hygiene primitive — never `http.Get` directly. Failure to do so is a regression in the threat model, not a stylistic issue.

**No credentials in logs.** Use `logging.Redact()` on any struct that holds session cookies, API keys, or `mam_id`. The Phase 0 redaction test still passes after Phase 1.

**Update `PROGRESS.md` continuously.** Same format as Phase 0. Every milestone gets a "Completed" entry.

**Test as you build.** Adapter tests use `httptest.Server` so they run offline. Don't write tests that hit the live tracker. A `make test` (or `go test ./...`) on a clean checkout MUST pass without network.

**Document deviations.** If `PROJECT.md` is ambiguous about a structural choice (which HTML parser, how to model the per-size seed-time requirement, etc.), pick one and write the choice and rationale into `DECISIONS.md`.

**Cookies live in `secrets`.** Tracker session cookies are stored via `secrets.Store.Set(ctx, "tracker:<id>:cookie", ...)`. Never in `trackers.config_json`. Never on disk in plaintext.

## What "Phase 1 Complete" Looks Like

After Phase 1 ships, the operator can:

1. Edit `config/trackers.yaml`, add a tracker entry with site URL, scrape interval, and a credentials reference. Save.
2. From the dashboard, run "Refresh now" (or wait for the scheduled interval).
3. Watch `journalctl -u command-center` show the scrape running, the hygiene timing, and the result.
4. Open the tracker's detail page in the PWA and see the ratio match what the tracker site shows.
5. Leave the system running for a day. Return to the detail page and see a ratio-over-time graph populated from `ratio_snapshots`.
6. Edit `config/trackers.yaml` to change the scrape interval. Save. Observe the change reflected in the next scrape cycle without restart.
7. Stop the service, delete the SQLite database, restart. The migrations re-apply, the schema is correct, and the snapshot history is gone but the tracker config is reloaded from YAML at boot.

The operator is now using the Command Center for one real thing every day. Every subsequent phase adds another thing.

## Begin

Read Phase 0's source first. Then create `migrations/002_phase1_trackers.sql` and the `internal/scrape` skeleton. Update `PROGRESS.md` after each subsystem lands. When the deliverables checklist is fully satisfied, write a final `PROGRESS.md` entry summarizing Phase 1 completion and stop. Do not begin Phase 2.
