From f76ee96ac4ed84292b5daaece65ea02bd2573212 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 20 Aug 2026 18:17:42 -0400 Subject: [PATCH] docs(jobs): the phone's band, and why it is in flow CLAUDE.md's jobs section said the header indicator is "the one view of everything at once, from every page"; that is now true on a desktop only, and the band is the phone's half. NOTES.md takes the measurement that decided the shape -- an overlay band at 424x439 is a lid, not a notification -- and the corollary about which tier can see it: ui-test, tsc, lint and the Go suite all passed on the broken version, and what failed was three e2e specs that have nothing to do with jobs. Run the suite, not the spec you wrote. --- .planning/NOTES.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 35 +++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/.planning/NOTES.md b/.planning/NOTES.md index 4fb55b8..188c78a 100644 --- a/.planning/NOTES.md +++ b/.planning/NOTES.md @@ -4302,3 +4302,51 @@ compiles against HEAD with a single shim (`SetPlaybackFinishedHandler` gained a `srcErr error` parameter), which makes "did the backend fix cause this" a ten-minute question instead of a full checkout. + +## An overlay band is not a notification, it is a lid (measured 2026-08-20) + +#62 asks for background jobs to become "a notification" on the phone, +and the app has exactly one notification surface, so the first version +of the fix put `` in `notification-host`'s band — which is +`position: fixed` under the header. It renders correctly, it is on top, +it is inside the viewport, and it is unusable. + +At the device's 424x439 viewport a **compact** panel showing two active +jobs is ~216px — half the screen — drawn over the content, with +`pointer-events: auto` so it swallows every tap underneath. Nothing in +the component tier could see it. The e2e suite could: four specs failed, +and *none* of them was about jobs — two `phone-shell` journeys into the +full-screen Now Playing and `header-action-overflow`'s phone case, all +three because the band was intercepting taps meant for the app. + +`` is in the shell's grid instead, as a row between the top +bar and the main panel, so it **pushes**. That is #24's one sentence +("no action is ever unreachable at any supported size") deciding a +layout question: a band that hides the app in order to say the app is +busy has traded the popover's fault for a worse one. + +Two things fell out of it worth keeping: + +- **A finished row in flow is furniture.** The overlay could afford to + keep terminal jobs around; a row that holds the content down after + the work is done cannot. `job-panel` grew `active-only` for the band, + and Settings keeps finished rows because that is where "did the last + scan work" is asked. +- **`job-row` already had the right density.** `variant="compact"` is + described in its own source as "the popover density", which is + exactly what the band is replacing — 216px against 259px for the + same two jobs, and no per-job statistics that a phone has no room + for. + +## The e2e suite is the tier that sees a shell regression (2026-08-20) + +Worth stating because it decided how #62 was verified. The change is +one component, one stylesheet and one line of `index.html`; `make +ui-test` (955 tests) passed on the broken overlay version and so did +`tsc`, `lint` and the whole Go suite. The failure was three specs that +have nothing to do with jobs, failing on `click()` timeouts. + +The corollary for anything that draws over the shell: **run the whole +e2e suite, not the spec you wrote.** A spec written for a feature +asserts the feature works; what a new overlay breaks is everything +else, and only the suite is looking at that. diff --git a/CLAUDE.md b/CLAUDE.md index 43b4d8b..ed31b8f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -593,11 +593,38 @@ rather than renaming them. `ClearFinishedJobs` is global — a Clear under Libraries would discard the index build's history too; a finished row dismisses itself. - The header `job-indicator` is untouched and is still the one view of - everything at once, from every page. One consequence worth knowing - before writing a spec: a section holding a `job-panel` also holds a - `job-details-drawer`, whose own header carries `.header` — so + The header `job-indicator` is still the one view of everything at + once, from every page — **on a desktop.** One consequence worth + knowing before writing a spec: a section holding a `job-panel` also + holds a `job-details-drawer`, whose own header carries `.header` — so `config-section .header` is ambiguous the moment a job exists. + + **Below 600px that indicator stands down and `` takes + over** (#62), because a popover is a *disclosure* and background work + is the one thing a phone should not make you open something to see — + and because #57 deletes the bar it is anchored to and is blocked on + it having somewhere else to live. The band is the same `job-panel`, + so `applyJobControl` and its index-build confirmation come along + rather than being reimplemented; `kinds="*"` is how it says "every + kind", which is what the indicator was for. + + Three things about it are load-bearing. **It is in the layout, not + over it**, as its own grid row above the main panel: the first + version put it in `notification-host`'s fixed band, which reads fine + in a screenshot and is unusable — at 424×439 a compact panel is + ~200px of a 439px screen and it *covers* what is under it, which four + e2e specs caught by failing on taps it was intercepting. **It shows + active work only** (`active-only`), because in flow a finished row is + furniture that keeps the content pushed down after the work is done; + finished rows stay where the work was started, which is #27's rule. + And **it renders nothing above 600px**, from `matchMedia` rather than + a media query, because that decides whether the element *exists* — + Settings already holds four `job-panel`s and a fifth answering for + every kind is `bottom-nav`'s "resolved to 2 elements" trap again. + `index.css` keeps it `display: none` outside the phone for a second + reason: an in-flow grid child with no named area is auto-placed into + one of the shell's rows, which is what the skip link is absolutely + positioned to avoid. - `config` — TOML-based settings. Settings page uses HTMX + templ for server-rendered HTML fragments. - `playlist` / `smartplaylist` — Playlist CRUD and rule-based smart playlists. - `mediacontrols` — OS media controls behind one `Handler`: MPRIS over