docs: open plan 009 and record what phase 1 found
Build & publish Arch package / arch-package (push) Successful in 2m4s
CI / check (push) Successful in 2m32s
Search index maintenance / maintain-index (push) Successful in 7s
CI / e2e (push) Successful in 6m21s

The plan's own framing was wrong in a way worth keeping: the badge was
not waiting on the download client, which had largely landed already —
it was waiting on somebody looking at a state nothing produced.
This commit is contained in:
2026-08-13 14:14:39 -04:00
parent 48f7795687
commit 979c6e83ed
2 changed files with 283 additions and 0 deletions
+68
View File
@@ -2196,3 +2196,71 @@ Six more things worth keeping:
hook* is what raises it — and a spec that spends the shared database
has to give it back, since the 90 specs share one backend in file
order.
## A state nothing produces is a state nobody has checked
Plan 009 phase 1: `library-status-indicator`'s third state, wired.
The generalisation: **an enum whose last value is never constructed is
not unfinished, it is wrong** — because everything around it has been
written, reviewed and tested against the two values that do occur, and
the code reads as complete from every angle except the one that
produces the third. `LibraryStatus` has had `queued` since it was
written: styled amber, given an hourglass, given the sentence "… is
queued for download". All eight call sites were a two-way ternary. So
an album on the request list rendered a plus and announced "is not in
your library" — on the same page, forty pixels from a filled button
reading "Wanted".
Nothing was going to find that. `make ui-test` and `make e2e` both
covered the badge; both asserted the states it produced. 007 phase 6
had rewritten this exact component, and the note it left behind
("when the download-client integration lands…") was itself the reason
nobody looked: it names a *future* condition for work that was already
possible, since `backend/download` was 16 541 lines and 20 bound
methods on the day it was written. **A written-down reason not to look
ages worse than the code it is about.**
Six more things worth keeping:
- **The second bug was in the screenshot of the first.** The "Wanted"
button rendered a question mark — the missing-icon fallback —
because `bookmark-check` is Font Awesome **Pro** and has never been
bundled. `offline-icons.spec.ts` asserts `__yjIconMisses` is empty
and passed the whole time: no spec had ever put the app in a state
where an album was requested. The bundled-icon design anticipated
exactly this ("twenty call sites compute their icon name from
state") and the *sweep* still could not see it, because a sweep only
sees the states it visits. Seventh regression in five plans that
only a PNG has caught, and the first found in a PNG taken of a
different bug.
- **A property that does not change does not re-render a child.**
`top-results-row` reads the request list, and its host handing back
the same `results` array means Lit stops at the property — the row
keeps its old badges while the store holds the right answer. The
virtualizer rule (`requestUpdate()` on host state) one level milder,
and the same fix: subscribe where the state is *read*.
- **A spec that gives state back has to be run twice to know it did.**
The `afterAll` cleanup called `callBinding`, which goes through
`window.__yjEvents` — installed by the `app` fixture, not by a bare
`browser.newPage()`. It threw where nothing was watching, left the
request behind and failed the *next* run with a stale `queued`. One
run proves the assertions; the second proves the teardown.
- **A freshly launched app cannot search its own catalog for ~40 s.**
The core artifact merge has to land (`core artifact: merge complete`
in `.dev/app.log`), and until it does Explore's search returns
nothing at all — *including for rows staged directly into
`explore_index` a moment earlier*, which makes it look like the
staging failed. Cost a cycle here reading as a failure of the neuter
the run was under. Budget 60 s, or wait for the log line.
- **The neuter has to be per line, not per feature.** Two fixes landed
together and each got its own one-line neuter, which is what made
the two failures distinguishable: one spec reported the wrong badge
status, the other reported `["bookmark-check"]`. Neutered together
they would both have failed and either could have been decorative.
- **The fix is where the rule is, and the rule was in eight places.**
Every one of the eight sites was individually reasonable; the third
state was missing from all of them because no site owns the
question. Same shape as `getCoverUrl()`, `track-index.ts` and
`page-header` — when a rule is written per call site, the call sites
do not disagree, they are all incomplete in the same way.