# Changelog v0.5.12 - 2026-06-02

### Added
- **Web Router Dashboard: full set manager.** The M4 read-only "Model Health"
  panel has been replaced with an interactive **Active Set** manager:
  - **Add model** picker with search + per-provider filter (147 routeable
    models from the new `/api/router/catalog` endpoint).
  - **Remove** button per row.
  - **Move up / Move down** buttons as a keyboard-accessible fallback.
  - **HTML5 drag-and-drop** to reorder priorities. Visual drop indicator
    (top/bottom border on the target row) shows where the row will land.
  - **Active set switcher** in the same header (when more than one set
    exists). Switches the daemon's `activeSet` via
    `POST /sets/:name/activate`.
  - **Save indicator** (`saving… / ✓ saved / ⚠ error`) shown next to the
    Add button so the user always knows whether the last drag actually
    persisted to disk.
- **"Sync best models" button.** One-click rebuild of the active set
  using the probe-based `sync-set` pipeline. Re-pings every candidate
  with the user's actual API keys, picks only the models that come back
  2xx with a reasonable latency, and persists the result. The Web UI
  surfaces a "Synced N working models from M probes" toast with the
  probe count. The button shows a "saving…" state while the probe runs
  (up to ~60s for 16 candidates).
- **Daemon endpoints for granular set management.**
  - `POST /sets/:name/models` — append a single model to a set.
  - `DELETE /sets/:name/models` — remove a single model by
    `{ provider, model }`.
  - `POST /sets/:name/reorder` — accept a full priority order
    `{ order: ["provider/model", ...] }` and re-number the set.
  - `POST /sets/:name/sync` — re-run `sync-set` against the named set
    with `maxProbes: 16, targetCount: 5` so the Web UI never spends
    more than ~60s on a sync.
  - `GET /api/router/catalog` — lightweight catalog of routeable
    models for the Add picker.
- **Default router set is now probe-driven.** `buildDefaultRouterSet`
  is async and accepts a `probeFn` that POSTs a 1-token chat completion
  to each candidate. Models that come back 2xx with low latency are
  pinned to the top of the set; failing models fall back to the static
  tier ordering. A new user with a half-broken key set now gets a
  default router set made of models that actually work, instead of a
  hardcoded NVIDIA-only list that 401s for everyone without a
  NIM-specific grant.
- **Web proxy for the new endpoints.** `/api/router/sets/:name/models`
  (POST/DELETE), `/api/router/sets/:name/reorder` (POST),
  `/api/router/sets/:name/activate` (POST), `/api/router/sets/:name/sync`
  (POST, 180s proxy timeout), `/api/router/catalog` (GET, with a
  fallback path that synthesizes the catalog from `sources.js` when the
  daemon is offline).
- **Switch case routing fix.** The `if/regex` blocks for parameterized
  routes (`/api/router/sets/:name/...`) were previously inside the
  request switch as `case` statements that never matched (JS switch
  is exact-string equality). Moved them above the switch so they
  actually run. This is why the original reorder/add/remove proxies
  returned 404 in the v0.5.11 web build.
- **6 new unit tests** for the granular set-management endpoints
  (add, duplicate, remove, reorder, reorder-with-missing-key,
  catalog) and **2 new tests** for `buildDefaultRouterSet`'s probe
  path (probe-preference + sync fallback). All 495 tests pass.

### Notes
- The TUI's `--sync-set` flag is unchanged — the Web "Sync best" button
  is the same pipeline with a smaller `maxProbes` so the UI stays
  snappy. CLI users still get the full 50-probe budget.
- The `fcm` virtual model that the Playground uses by default
  automatically picks up the new working-models set, so the
  Playground will start returning successful responses as soon as the
  Web user clicks "Sync best" once.
