docs(jobs): the phone's band, and why it is in flow
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m26s
CI / e2e (pull_request) Successful in 8m4s

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.
This commit is contained in:
2026-08-20 18:17:42 -04:00
parent 23f5a0c53a
commit f76ee96ac4
2 changed files with 79 additions and 4 deletions
+48
View File
@@ -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 `<job-panel>` 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.
`<job-band>` 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.
+31 -4
View File
@@ -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 `<job-band>` 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