# CLAUDE.md — Phase 5: autobrr Read Integration and Filter Performance

## Scope of This File

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

**Do not implement phases 6 through 15.** Stay in scope.

## Authoritative Specification

`PROJECT.md` §7.2 and §8.2 (filter performance correlation) are authoritative. Read them. Also read:
- `PROGRESS.md` for state.
- `DECISIONS.md` for binding decisions.
- The Phase 4 `internal/webhooks/autobrr.go` — Phase 5 turns the events that handler publishes into the derived metric that makes this product valuable.

## Phase 5 Mission

Filter-to-upload contribution correlation. The Command Center records every autobrr grab the operator's filters produce, then watches each resulting torrent's actual upload contribution over time and updates a per-filter performance score. Over weeks, this surfaces which filters are generating real upload (not just grabbing the most) and which are noise.

This is the first phase that delivers a metric no underlying tool produces.

## Deliverables Checklist

Phase 5 is complete when every item below is true:

- [ ] `internal/integrations/autobrr` provides:
    - REST API client (filters, releases history, announce-source status) using autobrr's documented HTTP API with API key auth.
    - The webhook receiver from Phase 4 is now its event ingress; the REST client is for reconciliation and configuration discovery.
- [ ] SQLite migration `006_phase5_automation.sql` adds the `automation_tools` and `filter_performance` tables exactly as in `PROJECT.md` Appendix A. The `notification_rules` table is **out of scope** for Phase 5 (it lands in Phase 6) — do not pre-create it.
- [ ] On every `autobrr.grab` event from the bus, write a `filter_performance` row with `grabbed_at`, `filter_external_id`, `filter_name`, `release_name`, and `info_hash` (if known at grab time; sometimes only known post-add).
- [ ] A linker job correlates each `filter_performance` row to the torrent that resulted from the grab. The link is by info_hash when present; otherwise by name fuzzy match within a configured time window (default 5 minutes). Document the matching algorithm in `DECISIONS.md`.
- [ ] A scoring job runs on a configurable cadence (default hourly) and updates `final_uploaded_bytes`, `final_ratio`, and `last_measured_at` for each linked row by querying the latest `torrent_snapshots` (DuckDB query). For torrents still in flight, the score reflects current state, not final; the column name acknowledges this is a moving target until the torrent is deleted or stops earning.
- [ ] API endpoints (auth required, under `/api/automation`):
    - `GET /api/automation/tools` — list configured automation tools and their connection status.
    - `GET /api/automation/tools/:id/status` — single tool detail.
    - `GET /api/automation/filters` — list filters from all configured tools.
    - `GET /api/automation/filters/:id` — one filter's recent activity + computed score.
    - `GET /api/automation/filters/:id/performance?range=...` — performance over time window.
    - `GET /api/automation/releases?filter=...` — recent release history (paginated, via autobrr's API).
    - `PATCH /api/automation/filters/:id` — toggle enabled, but mutation is delegated to autobrr's API (Command Center does not maintain a parallel filter store).
- [ ] Frontend:
    - `/automation` route renders an automation overview: tools, filters, recent grabs timeline.
    - `/automation/filters/:id` route renders the filter's performance dashboard: grabs/day, upload contribution per grab (median + p95), score relative to other filters.
    - Score visualization: a quadrant chart (grab rate vs. upload contribution per grab) with each filter as a labeled point. Filters in the bottom-right (high grabs, low upload) are flagged.
- [ ] Tests: linker correctly pairs grabs to torrents (by hash, by fuzzy name, with timeout); scorer updates filter_performance rows from synthetic snapshots; API endpoints return the right shape; quadrant chart computes from a fixture.
- [ ] `PROGRESS.md` updated. `DECISIONS.md` records the linker algorithm, the scoring window, and any cross-seed-related score-credit decisions (cross-seed-injected torrents count toward the source filter's score — see PROJECT.md §7.3).

## Phase 5 Database Scope

Migration `006_phase5_automation.sql` creates:
- `automation_tools`
- `filter_performance`

`notification_rules` is Phase 6. Do not pre-create.

## Repository Layout

```
internal/
  integrations/
    autobrr/
      client.go              # REST API client
      types.go               # request/response shapes
      reconcile.go           # periodic reconciliation against the REST API
      *_test.go
  performance/
    linker.go                # grab → torrent linking
    scorer.go                # snapshot → final_uploaded_bytes
    *_test.go
```

Files added under existing packages:
- `internal/server/routes_automation.go`
- `internal/webhooks/autobrr.go` — extended to call `linker.RecordGrab(...)`.

Frontend additions:
- `web/src/pages/Automation.tsx`
- `web/src/pages/FilterDetail.tsx`
- `web/src/components/QuadrantChart.tsx`

## Working Rules

**Don't reimplement autobrr.** Filter mutation goes through autobrr's API. The Command Center observes and scores.

**Fuzzy linking is bounded.** If a grab has no info_hash and the time-window fuzzy match fails, mark the row `unlinked` and move on. Periodically retry up to a configured limit. Do not let unlinked grabs accumulate forever.

**Score honestly.** A filter that produced one grab a week ago with massive upload looks "great" by per-grab average. Surface confidence intervals where the sample size is small. The scorer returns `(score, sample_size)` not just a number; the UI displays both.

**Cross-seed credits.** A torrent injected by cross-seed counts toward the source filter's score (PROJECT.md §7.3). The linker handles this by following the `torrents.cross_seed_origin_hash` column populated by Phase 4's cross-seed webhook handler.

**No premature optimization on autobrr's API.** It supports incremental polling for the activity log; use it. But the primary data source is the webhook stream from Phase 4; the REST client is for filter definitions and reconciliation.

## What "Phase 5 Complete" Looks Like

After Phase 5 ships, the operator can:

1. Add an autobrr tool reference in `config/automation-tools.yaml` (API key stored in `secrets`).
2. Watch `/automation` populate with the operator's existing autobrr filters.
3. Trigger a grab from autobrr (or wait); see the grab appear in the activity timeline within seconds.
4. After a few days, open `/automation/filters/:id` and see a real score: grabs/day, median upload per grab, score relative to peer filters.
5. Identify a filter that grabs a lot but produces little upload (the quadrant chart shows it in the "tighten" zone) and another that grabs little but each grab earns big (the "relax" zone).
6. Filter mutation: change a filter's parameters in autobrr's own UI; reconciliation reflects the change in the Command Center within 15 minutes.

## Begin

Read `PROJECT.md §7.2` and `§8.2`. Implement the autobrr REST client first, then the linker, then the scorer. Wire the webhook handler from Phase 4 to call the linker on every grab event. Then build the UI. Stop at the end of Phase 5.
