# CLAUDE.md — Phase 9: tqm and cross-seed Integration

## Scope of This File

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

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

## Authoritative Specification

`PROJECT.md` §7.3 (cross-seed) and §7.4 (tqm) are authoritative. Read both. Also read:
- `PROGRESS.md` and `DECISIONS.md`.
- The Phase 4 webhook handler skeletons for cross-seed — Phase 9 extends them.

## Phase 9 Mission

Delegate cleanup to tqm and surface its activity. Surface cross-seed configuration and matching activity in the dashboard. The Command Center does not reimplement tqm's rule evaluation engine or cross-seed's matching logic; it manages their configs, captures their outputs, and adds the cross-cutting context the operator needs.

This is a "delegation" phase: most of the value comes from how the Command Center *presents* tqm and cross-seed activity, not from new computation.

## Deliverables Checklist

Phase 9 is complete when every item below is true:

- [ ] tqm config management: `config/tqm-rules.yaml` is the operator's source of truth. The Command Center watches it, validates against tqm's schema (subset thereof; document any divergence in `DECISIONS.md`), and uses the file as input to the simulation engine from Phase 8.
- [ ] tqm run recording: the Command Center receives tqm run outputs via a webhook hook the operator installs in their tqm cron wrapper. The hook posts a JSON summary (torrents touched, action taken per torrent, run id, duration). Recorded into `audit_log` and a new `tqm_runs` view backed by audit_log query (no separate table).
- [ ] cross-seed config management: similarly, `config/cross-seed.yaml` is watched and validated; cross-seed's own webhook posts to `/webhook/crossseed/:id` (Phase 4 wired the route, Phase 9 actually processes the events).
- [ ] cross-seed match handling:
    - `crossseed.match_found` event → record in `audit_log`, surface in activity timeline.
    - `crossseed.match_applied` event → update the destination torrent's `torrents.cross_seed_origin_hash` so Phase 5's filter performance correctly credits the source filter for the injected torrent's upload.
- [ ] API endpoints (auth required):
    - `GET /api/tqm/recent-runs?range=...` — recent runs and per-run summary.
    - `POST /api/tqm/dry-run` — operator-triggered tqm dry-run against current rules (delegates to tqm CLI via `os/exec`; returns parsed output).
    - `GET /api/cross-seed/activity?range=...` — recent match-found and match-applied events.
    - `GET /api/cross-seed/stats` — per-tracker hit rate over the configured window.
    - `POST /api/cross-seed/search` — trigger cross-seed to search for matches for one or more torrents (delegates to cross-seed's own API).
- [ ] Frontend:
    - `/cross-seed` route: matches found timeline, per-tracker hit rate chart, backlog of unmatched torrents.
    - `/tqm` route: recent runs, run detail (which torrents, what action), the option to trigger a dry-run.
    - Both views integrate with the filter performance view from Phase 5 (cross-seed-injected torrent uploads visible there).
- [ ] tqm and cross-seed tools register themselves in `automation_tools` (table already exists from Phase 5). The integration health checks (Phase 6's `automation_disconnected` trigger) cover them.
- [ ] Tests: webhook handlers correctly update torrents.cross_seed_origin_hash; tqm run summary parser handles edge cases (zero torrents touched, errors); dry-run exec wrapper sandboxes and times out properly.
- [ ] `PROGRESS.md` and `DECISIONS.md` updated. `DECISIONS.md` records: how the dry-run exec wrapper limits resource usage, where tqm's binary is expected to live (PATH or configured), the policy on running tqm under the systemd unit's hardened sandbox.

## Phase 9 Database Scope

No new tables. Phase 9 uses:
- `audit_log` (Phase 0) — for tqm run records and cross-seed events.
- `torrents` (Phase 1) — `cross_seed_origin_hash` populated by cross-seed match events.
- `automation_tools` (Phase 5) — tqm and cross-seed registered.
- `webhook_endpoints` (Phase 4) — webhook tokens.

A migration is not required for Phase 9 unless an indexing concern surfaces during development.

## Repository Layout

```
internal/
  integrations/
    tqm/
      client.go              # exec wrapper for tqm dry-run
      runparser.go            # parse tqm output into structured run summary
      *_test.go
    crossseed/
      client.go              # REST API wrapper for cross-seed
      types.go
      *_test.go
config/
  tqm-rules.yaml
  cross-seed.yaml
scripts/
  tqm-hook-template.sh       # operator-installed cron wrapper that posts run summaries
```

Files added under existing packages:
- `internal/server/routes_tqm.go`
- `internal/server/routes_crossseed.go`
- `internal/webhooks/crossseed.go` — fully implemented (Phase 4 was a stub).

Frontend additions:
- `web/src/pages/CrossSeed.tsx`
- `web/src/pages/Tqm.tsx`
- `web/src/components/TqmRunDetail.tsx`

## Working Rules

**The Command Center owns no rule evaluation.** Filter rules are tqm's. Match rules are cross-seed's. The Command Center stores configs, captures outputs, and surfaces context.

**Sandbox exec.** The tqm dry-run wrapper sets resource limits (cpu time, memory) and timeouts. Never trust the operator to have a sensible default; enforce caps.

**Cross-seed credit is load-bearing.** Phase 5's filter performance score depends on `cross_seed_origin_hash` being populated. Verify this end-to-end with a test that injects a synthetic match event and observes the credit flow into a filter's score.

**Dry-run is read-only.** The dry-run endpoint passes tqm's `--dry-run` flag (or equivalent). No production state changes are possible through this path.

**tqm runs on its own cadence.** The Command Center does not schedule tqm. The operator's cron / systemd timer fires tqm; the wrapper script posts the result. The Command Center has no opinions about when tqm runs.

## What "Phase 9 Complete" Looks Like

After Phase 9 ships, the operator can:

1. Place a new `config/tqm-rules.yaml` with a tightened rule. Validation passes; hot-reload applies.
2. Tap "Dry run" in the Tqm UI. See exactly which torrents would be removed under the current rules, with a per-torrent reason.
3. Schedule tqm to run via the operator's cron with the hook script installed. After the next run, see the result in the Tqm UI: "Removed 3, tagged 12, errored 0."
4. Open the cross-seed page. See the hit rate per tracker: "TrackerA: 67% / 30d, TrackerB: 41% / 30d". Open the activity timeline to see recent matches.
5. Trigger a cross-seed search for a specific torrent from its detail page. The Command Center delegates to cross-seed's API; the result appears in cross-seed's own activity stream within seconds.
6. Open the filter performance view from Phase 5. A filter whose grabs frequently cross-seed shows the cross-seed contribution credited correctly.

## Begin

Read `PROJECT.md §7.3` and `§7.4`. Start with cross-seed's webhook handler (extending the Phase 4 stub) — that path is what makes Phase 5's filter scoring whole. Then tqm: the run-summary parser, then the dry-run exec wrapper, then the UI. Stop at the end of Phase 9.
