# Handoff — fleet engine (mesh stage 1+3 core) + MLX TitleRefiner

Session date: 2026-06-09 (evening). Scope: "do everything left on the roadmap
that is buildable from this repo." Everything below is committed on `main`
(swept into autocommits `8f12f47` "local llm title refiner integration" and
`7ff780f` "restart command support" — the autocommit daemon mixes sessions'
files; the commit titles do NOT map cleanly to this work).

## What landed

### 1. Fleet engine — `governor/src/fleet/` (TS/Bun)

The implemented single-fleet core of the mesh design spec
(`../history/20260608_fleet-manager-mesh-design.md`):

| File | Owns |
|---|---|
| `types.ts` | FleetHost / Duty / Source / Peer / Holding / verdict types (mirror the spec entities) |
| `registry.ts` | ingest: `~/.config/tv-anarchy/fleet.json` **array form is authoritative** (the app-side fleet registry written by the Devices-tab session — knows apricot/phone); `devices.json` fallback; optional policy keys `floorCopies` / `sources` / `staticHoldings` / object-form `devices` overrides |
| `duties.ts` | deterministic duty assignment (broadcast / f2f_relay / public_swarm_face) + invariants (consumer never gets a duty; one broadcast; seedbox-first face; home-IP exposure warned); `diffDuties` for change logs |
| `custody.ts` | N-copy floor-check (default 2), rolling-baton custodianship, ≥1 always-on slot, `custodians_of`, re-pin **plans** |
| `reaper.ts` | healthy\|stalled\|dead (idle 30 min / 72 h), mesh-first recovery upgrade, re-search fallback |
| `peers.ts` | source model with BOTH private-tracker gates (`search_only` default-closed; `f2f_only` FORCED un-overridable) + `peers_for` (fleet ∪ seedbox ∪ live DHT, provenance-tagged, deduped) |
| `transmission.ts` | ssh→localhost:9091 JSON-RPC view of black's daemon (vitals, holdings, live peers, reannounce/verify/start) |
| `cli.ts` | `portable-net-tv fleet status\|duties\|custody\|reaper [--apply]\|peers <q>` (all `--json`); state diff at `~/.local/state/tv-anarchy/fleet-state.json` |

Read-only by default. The ONLY mutating path is `fleet reaper --apply` =
idempotent transmission nudges. Re-pins / mesh recoveries / re-searches are
printed plans by design.

### 2. MLX TitleRefiner — seam closed

- `recommender/media_rec/title_refiner.py` — MLX Qwen2.5-1.5B (same model as
  the grouper), prompt → `{"title": ...}`, plausibility guard (must share a
  token with the filename), degrades to empty title without MLX.
- `Sources/TVAnarchyCore/Metadata/LocalLLMTitleRefiner.swift` — shells uv like
  `LocalLLMGrouper`; disk cache `~/.local/state/tv-anarchy/title-refinements.json`
  (empties cached too); 2-consecutive-failure session kill-switch.
- Wired in `TVAnarchyApp.init()`.
- **Bug fixed en route:** `FilenameParser.extractTitle` fell back to the raw
  filename BEFORE the `<2 chars → refiner` check, so the refiner was
  unreachable. Fallback now runs after the consult.

### 3. Docs aligned

`docs/roadmap.md` (status table + build order rewritten), `docs/architecture.md`
(§3 fleet engine, §2 refiner, §4 retitled), `docs/data-model.md` (fleet.json
schema as found on disk), `docs/operations.md` (fleet CLI section),
`fleet/README.md` (points at the implementation).

## Verification (all green at handoff)

- `cd governor && bun test` → **45 pass** (5 files: duties/custody/reaper/peers/registry)
- `cd governor && bunx tsc --noEmit` → clean
- `xcodebuild -scheme TVAnarchy test` → **149 pass** (incl. 2 new refiner-seam tests)
- Live: `fleet status` (4-member registry from real fleet.json), `fleet reaper`
  against black = 218 torrents → 180 healthy / 32 stalled / 6 dead,
  `fleet custody` = every title breaches 2-copy floor (true: black is the only
  custodian), `title_refiner.py "[Anime Time] Sousou no Frieren - …"` →
  `"Sousou no Frieren"` on real MLX.

## What's NOT done (and why)

1. **Re-pin actuation** — executing cross-host copies + auto-feeding `research`
   actions into `search/`. Plans print today. This is the next real step;
   build it in governor (transfer queue territory), never in the Swift app.
2. **Fleet WG fabric (plane 1)** — BLOCKED on an open user decision: is
   `10.9.0.4` a general overlay (fleet WG additive) or ad-hoc tv-anarchy
   (fleet WG replaces)? Also needs root on each node. Spec section
   "Networking — two independent planes."
3. **Seedbox / friend-mesh / private-tracker / Discord** — blocked on external
   infrastructure (a provisioned box, other fleets, creds, bot tokens). The
   engine already models seedbox class/duties and enforces the private gates.

## Gotchas for the next session

- **Autocommit daemon**: your working tree WILL be committed under another
  session's message mid-flight. Check `git log` before assuming anything is
  uncommitted; don't be surprised when your diff vanishes.
- **fleet.json is app-owned** (array form, written by the Devices-tab work in
  a different worktree — no writer exists in THIS tree's Sources). The governor
  only reads it. Policy keys (floorCopies/sources/staticHoldings) are additive
  top-level keys; if the app-side writer ever rewrites the file wholesale,
  check it preserves unknown keys.
- **fleet.json says `reachable: home_lan` for black/apricot**, so no f2f_relay
  duty is assigned (needs wireguard|public_ip). The engine is correct; the
  registry data is conservative. Flip `reachable` to `wireguard` (or override
  in object form) to see relay duties.
- **Swift test suite writes the real `~/.config/tv-anarchy/devices.json`**
  (loadOrSeed) — back it up before config/migration verification.
- Reaper thresholds: STALL_AFTER 30 min, DEAD_AFTER 72 h idle (incomplete +
  peerless). Complete torrents are always healthy.
- transmissionHost = first registry host with `transmission_rpc` AND an ssh
  destination — apricot advertises transmission but has no `user@host` service
  detail, so black is the working host today.
