diff --git a/.planning/NOTES.md b/.planning/NOTES.md index c7d3adf..3c0fa9c 100644 --- a/.planning/NOTES.md +++ b/.planning/NOTES.md @@ -3732,3 +3732,22 @@ so it is the mechanism and not a test-only door. Use the nav item when the *nav* is the subject, and `navigateTo` when the view is. + +## `config-section .header` is ambiguous once a job exists (2026-08-19) + +#27 embeds `` inside four Settings sections, and a panel with +any job in it also mounts a `job-details-drawer` — whose own header +carries the class `.header`. So `config-page config-section .header`, +which `settings-reach.spec.ts` had used since plan 007, resolves to two +elements and fails Playwright's strict mode the moment a scan has run. + +Two things follow. A spec asserting on a section's *disclosure* should +locate it by role and name (`getByRole('button', {name: heading})`) or +scope per section and take `.first()`, not by that class. And this is a +worked example of the more general trap: a class name is not a +selector's contract, and a component that embeds another inherits its +class names into every ancestor query. + +It also only appears in a suite that has *done* something — the +sections are empty on a fresh app, so this cannot be reproduced by +opening Settings and looking. diff --git a/CLAUDE.md b/CLAUDE.md index 4664818..1052624 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -514,6 +514,36 @@ rather than renaming them. the autotag apply are registered; anything that is not registered has none of that, which is exactly how the three gaps the audit found came about. + + **Its rows are shown where the work is started, not on a page of + their own.** #27 folded the Jobs destination away, and the shape it + folded into is `` embedded four times — scans in + Settings → Libraries, index and enrichment in Settings → Search + Index, downloads under the download clients, the autotag apply in + `autotag-view`. One "Background jobs" section in Settings was the + obvious reading of the report and is the tab again under another + name. + + Four things about it are load-bearing. **Four of the five kinds + already had a home** that showed their work — the tier list, the + download list, the apply ring — and what none of them had is the + *generic* affordances, so the panel carries pause, cancel, Details + and the log to each rather than replacing what is there. **The + controls are `applyJobControl`**, not a reimplementation, which is + what keeps the "you will discard hours of downloading" confirmation + alive: it is keyed on `KindIndexBuild` inside the shared handler, and + a host drawing its own buttons would drop it silently. **A panel with + nothing to say is `hidden`**, host margin included, because an idle + panel in four places is four pieces of furniture describing an + absence. And **there is no "Clear finished"** in it, because + `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 + `config-section .header` is ambiguous the moment a job exists. - `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 @@ -1086,6 +1116,15 @@ tap away. Which four tabs is still plan 016's committed subset; this only removes from it, and "More" is never filtered because it is how everything else stays reachable. +**A retired destination is the one shape this does not make free.** An +absent visibility key takes its default and an unknown one is dropped, +but `DefaultPage` is a *value*: a launch page naming a view that no +longer exists fails validation, and on the load path that means the app +refuses to start for whoever had it selected. `RetiredViews` is that +list, and `ApplyDefaults` treats a retired name as a zero value while +an unknown-but-not-retired one still errors — a typo is worth being +told about. #27 retiring `jobs` is its first entry. + **The list of destinations is `services/view-meta.ts`**, on `shortcut-meta.ts`'s pattern, because #25 gave it a second reader: Settings renders a toggle per view and needs the same labels in the