# The dashboard (`/`) The front door. It used to be a doorway that put you back in the song pile you last judged; it is a page now, and the song piles are one nav entry (`song ▸`) at the URLs they always had. **Nothing on it shells out.** Every panel reads the index, the two in-memory job registries, or a cache. The one probe on the page is behind a button. | panel | what it reads | where it goes | |---|---|---| | **now** | `runningActivity()` — whichever of the two job registries (`lib/jobs.ts` chain jobs, `lib/mix.ts` renders) is busy, merged in `lib/activity.ts`; step `n/of` and per-entry state from the build's NDJSON events. A client poll of `/api/jobs` every 2 s **only while something runs**. Under it, the newest finished jobs. | the project page, or `/mix` | | **needs a decision** | `openDecisions()` — the same reducer as `/browse/decisions`; totals and the worst eight | `/browse/decisions` | | **projects** | `listProjects()` + `decisionCounts()`; one row per kind, the six states as proportional segments. The header string comes from `lib/dashboard.ts`, shared with `/browse`, so the two cannot disagree. One meaningful colour: `stale` is `--color-dirty`; every other state is neutral. | `/browse?kind=&state=` | | **sources** | each report project's summary attrs (`sources-checked-at`, `sources-dead`, `sources-missing`, `cue-gaps`), written by `lib/projects/report.mjs`: never checked / older than 30 d / with dead or missing sources. "Re-check" starts one `check-sources` job. | the project page | | **deliverables** | `attrs["final-bytes"]` (a report's `out/.mp4` + `-full.mp4`; a song's shipped cuts) — built count, bytes, newest, stale | `/browse?state=built` | | **tools & disk** | the doctor **cache** (`lib/doctor.ts`, ten minutes) or "not checked"; free bytes under `REPORTS_ROOT` via `statfs`; index health. The button `POST`s `/api/doctor`, which is the only place the app probes. Red only when the report pipeline needs a tool and it is absent. | — | | **recent** | the six newest projects by `newestMtimeMs`, as the same cards `/browse` draws | the project page | | **resume** | the last URL the recorder wrote (`components/LastPage.tsx`), HEAD-probed down to `/browse`, offered as a chip rather than navigated to | wherever you were | `?from=/` opens the new-project menu prefilled — the editor's "Open in umtool" link (`UMTOOL_URL`) lands here. ## `/api/jobs` `GET` → `{ running, jobs }`, one shape over both registries, `no-store`. The registries stay separate: their one-at-a-time rules differ (a chain is process-wide exclusive; a render is one ffmpeg call). ## `umtool doctor` The CLI form of the tools panel: `umtool doctor [--json]`, exit 1 if the report pipeline is missing a tool it cannot run without (ffmpeg, ffprobe, yt-dlp, qrencode). ImageMagick is probed as `magick` — every call in the pipeline is IM7's `magick`, and a machine with only `convert` cannot build a card, which the report says.