Roadmap: suggested order for the desktop + Android backlog (#5–#72) #73

Open
opened 2026-08-18 14:38:32 +00:00 by logan · 14 comments
Collaborator

Meta issue for the 67 reports filed as #5–#72. It records the order they should be worked in and the soft relations that are not expressible as blockers. Hard blockers are set as real Gitea issue dependencies and render on each issue.

Labels in use: Kind/*, Platform/*, Priority/*, Area/*, Reviewed/Confirmed (I read the code and confirmed the defect), Status/Blocked (has a dependency).


Hard dependency graph

#16 badge accuracy ──► #7  full tracklist toggle
                   └─► #38 make unowned obvious ◄── #34 icon language
#5  list selection ──► #8  drag tracks to queue
#11 desktop full-screen NP ──► #44 lyrics
#22 logo ──► #50 README
#24 window sizes / queue model ──► #55 queue as a screen ──► #59 slim mini player
#62 jobs as a notification ──► #57 drop the Android top bar
#60 bottom-sheet menus ──► #63 touch gestures, #67 links into menus, #71 "More" nav
#18 per-shelf refresh ──► #70 stop reshuffling home shelves

Phase 0 — cheap, confirmed, no dependencies

Each is a small, isolated fix I traced to a specific line. Good warm-up work and several are user-visible immediately.

  • #33 "Want track" does nothing — the downloadStore subscription never triggers a re-render
  • #13 seek bar reflows on digit change — tabular-nums
  • #9 drop the "Tracklist" heading
  • #29 album year truncated with the name
  • #41 KDE shows the previous album's art
  • #19 drag image needs a count badge
  • #26 queue toggle has no active state
  • #37 wanted-list checks with no download client

Phase 1 — correctness of "what do I own"

The single most-reported theme. Do it in this order; the later ones are wrong until the earlier ones are right.

  1. #16 badge accuracy (tagwriter must write track/disc totals)
  2. #7 full catalog tracklist with missing tracks shown
  3. #34 icon language
  4. #38 make unowned content obvious everywhere
  5. #17 demote the release-version selector (same page, do it in the same pass)
  6. #28 high-confidence autotag CTA (depends on nothing, but only makes sense once the page tells the truth)

Phase 2 — desktop shell and responsiveness

#24 is the design decision the rest hang off; settle the queue-panel model before anything else here.

  • #24 supported sizes + queue overlay model → then #69 (page-header overflow, also fixes the Android report)
  • #6 global back/forward, then #72 (nav highlight, Android, same mechanism)
  • #25 configurable sidebar tabs → unlocks #27 (Jobs into Settings) and relates to #37
  • #48 top bar rework — soft relation to #22, the "YJ" mark may become the icon
  • #23 centred transport, #42 inline volume, #30 track-list chrome

Phase 3 — Android chrome

Sequence matters: the top bar cannot go until the job indicator has somewhere to live, and the queue cannot leave the mini bar until it is a screen.

  1. #62 jobs as a notification
  2. #57 drop the top bar, search becomes a modal
  3. #55 queue as a screen → #59 slim the mini player → #61 drop the tiny links
  4. #56 control sizes, #58 progress line
  5. #51 small-screen support — the umbrella; verify it after the above, since each buys vertical space. Deliberately not blocked, because part of it (width audits, min-width: 0) can start now
  6. #52 crash on resume — Priority/Critical, independent of all of it, needs a real device and logcat

Phase 4 — Android touch model

  • #60 bottom-sheet menus first (it is the presentation every other item needs)
  • then #63 gestures, #67 links into menus, #71 "More" nav
  • #54 native feel and #68 hover-only affordances are the polish pass over the same surfaces
  • #66 album page scrolling, #65 art preloading, #64 no in-app volume — independent

Phase 5 — features

  • #31 play all / shuffle all (soft relation to #14 — set a correct Source while you are there)
  • #35 smart playlist OR
  • #32 artist sort keys and #46 date-added sort — do together, both need the same backend columns
  • #15 alphabet jump strip
  • #20 collapsible playlists in the sidebar
  • #36 download destination, #39 column config, #40 settings search
  • #45 auto-DJ (soft relation to #14 — the queue's Source is how it announces itself)
  • #49 art scaling (soft relation to #29 and #65 — same card layout)
  • #10 pinned large art, #11 desktop full-screen NP → #44 lyrics, #12 mini-player window

Phase 6 — presentation and docs

  • #22 logo → #50 README
  • #47 prebuilt themes incl. OLED
  • #70 home shelves (after #18)

Soft relations, for the record

  • #14 (stale "Playing from") feeds #31 and #45 — anything that builds a queue should set an honest Source.
  • #42 and #64 both touch volume-control; the Android answer is "remove it", the desktop answer is "inline it".
  • #29, #49 and #65 are all the album card's layout and image tier.
  • #27, #40 and #25 all reshape Settings.
  • #5, #43 and #63 are three different answers to "how does selection work in a list" — desktop, the queue, and touch. Keep them consistent with utils/selection-controller.ts rather than growing three models.
Meta issue for the 67 reports filed as #5–#72. It records the **order** they should be worked in and the **soft relations** that are not expressible as blockers. Hard blockers are set as real Gitea issue dependencies and render on each issue. Labels in use: `Kind/*`, `Platform/*`, `Priority/*`, `Area/*`, `Reviewed/Confirmed` (I read the code and confirmed the defect), `Status/Blocked` (has a dependency). --- ## Hard dependency graph ``` #16 badge accuracy ──► #7 full tracklist toggle └─► #38 make unowned obvious ◄── #34 icon language #5 list selection ──► #8 drag tracks to queue #11 desktop full-screen NP ──► #44 lyrics #22 logo ──► #50 README #24 window sizes / queue model ──► #55 queue as a screen ──► #59 slim mini player #62 jobs as a notification ──► #57 drop the Android top bar #60 bottom-sheet menus ──► #63 touch gestures, #67 links into menus, #71 "More" nav #18 per-shelf refresh ──► #70 stop reshuffling home shelves ``` --- ## Phase 0 — cheap, confirmed, no dependencies Each is a small, isolated fix I traced to a specific line. Good warm-up work and several are user-visible immediately. - #33 "Want track" does nothing — the `downloadStore` subscription never triggers a re-render - #13 seek bar reflows on digit change — `tabular-nums` - #9 drop the "Tracklist" heading - #29 album year truncated with the name - #41 KDE shows the previous album's art - #19 drag image needs a count badge - #26 queue toggle has no active state - #37 wanted-list checks with no download client ## Phase 1 — correctness of "what do I own" The single most-reported theme. Do it in this order; the later ones are wrong until the earlier ones are right. 1. #16 badge accuracy (`tagwriter` must write track/disc totals) 2. #7 full catalog tracklist with missing tracks shown 3. #34 icon language 4. #38 make unowned content obvious everywhere 5. #17 demote the release-version selector (same page, do it in the same pass) 6. #28 high-confidence autotag CTA (depends on nothing, but only makes sense once the page tells the truth) ## Phase 2 — desktop shell and responsiveness #24 is the design decision the rest hang off; settle the queue-panel model before anything else here. - #24 supported sizes + queue overlay model → then #69 (`page-header` overflow, also fixes the Android report) - #6 global back/forward, then #72 (nav highlight, Android, same mechanism) - #25 configurable sidebar tabs → unlocks #27 (Jobs into Settings) and relates to #37 - #48 top bar rework — soft relation to #22, the "YJ" mark may become the icon - #23 centred transport, #42 inline volume, #30 track-list chrome ## Phase 3 — Android chrome Sequence matters: the top bar cannot go until the job indicator has somewhere to live, and the queue cannot leave the mini bar until it is a screen. 1. #62 jobs as a notification 2. #57 drop the top bar, search becomes a modal 3. #55 queue as a screen → #59 slim the mini player → #61 drop the tiny links 4. #56 control sizes, #58 progress line 5. #51 small-screen support — the umbrella; verify it *after* the above, since each buys vertical space. Deliberately not blocked, because part of it (width audits, `min-width: 0`) can start now 6. #52 crash on resume — `Priority/Critical`, independent of all of it, needs a real device and `logcat` ## Phase 4 — Android touch model - #60 bottom-sheet menus first (it is the presentation every other item needs) - then #63 gestures, #67 links into menus, #71 "More" nav - #54 native feel and #68 hover-only affordances are the polish pass over the same surfaces - #66 album page scrolling, #65 art preloading, #64 no in-app volume — independent ## Phase 5 — features - #31 play all / shuffle all (soft relation to #14 — set a correct `Source` while you are there) - #35 smart playlist OR - #32 artist sort keys and #46 date-added sort — **do together**, both need the same backend columns - #15 alphabet jump strip - #20 collapsible playlists in the sidebar - #36 download destination, #39 column config, #40 settings search - #45 auto-DJ (soft relation to #14 — the queue's `Source` is how it announces itself) - #49 art scaling (soft relation to #29 and #65 — same card layout) - #10 pinned large art, #11 desktop full-screen NP → #44 lyrics, #12 mini-player window ## Phase 6 — presentation and docs - #22 logo → #50 README - #47 prebuilt themes incl. OLED - #70 home shelves (after #18) --- ## Soft relations, for the record - #14 (stale "Playing from") feeds #31 and #45 — anything that builds a queue should set an honest `Source`. - #42 and #64 both touch `volume-control`; the Android answer is "remove it", the desktop answer is "inline it". - #29, #49 and #65 are all the album card's layout and image tier. - #27, #40 and #25 all reshape Settings. - #5, #43 and #63 are three different answers to "how does selection work in a list" — desktop, the queue, and touch. Keep them consistent with `utils/selection-controller.ts` rather than growing three models.
logan added the Kind/DocumentationPlatform/AndroidPlatform/Desktop
Priority
High
2
labels 2026-08-18 14:38:36 +00:00
Author
Collaborator

Phase 2's anchor is done, and so is the item hanging off it.

main is green at c84a906 with no open PRs. Since this list was last
reconciled:

  • #24 — supported sizes + queue overlay model (PR #132). The three
    bands are written down as a promise, and the queue is an overlay when
    available − panelWidth < 480 rather than at a breakpoint, because
    the panel's width is user state.
  • #69page-header overflow (PR #136). Needed an actions API
    first, not a rule added in one place: a component cannot move another
    component's light-DOM children into a dropdown and keep their
    behaviour. That is why it was its own PR rather than part of #24.

So Phase 2's first bullet is complete and the next in the stated
order is #6 (global back/forward) → #72.

One thing worth checking before anyone claims #6: the back stack may
already exist. index.ts records each navigation with pushState and
replays popstate, and the old navStack was deleted rather than kept
beside it — that landed for Android's hardware back button. #6 asks for
global back/forward, so it is plausibly now a smaller job than this
list assumes, or already partly satisfied. Someone should read it
against the code before estimating it.

Two Phase 3 items were taken out of order and are closed (#61, #68,
in PR #139's quick-wins batch). Both were small and independent, so no
sequence was broken — but noting it here so the phase list is not read
as still owing them. #55 became unblocked at #24 and is still open, with
the overlay to build on.

Two new issues were filed against this backlog and want sequencing:

  • #137 — three more hover-only affordances (queue remove, cover-art
    edit, shortcut reset). The correct fix is the opposite of #68's: on
    touch they must be always visible, since hiding them removes the only
    route to the action. Sequenced after #60.
  • #138play-in-context.test.ts flakes ~1 in 3 on pristine
    main. Not in any phase; it is infrastructure, and it can fail CI on
    an unrelated PR, which is how it was found.
**Phase 2's anchor is done, and so is the item hanging off it.** `main` is green at `c84a906` with no open PRs. Since this list was last reconciled: - **#24** — supported sizes + queue overlay model (PR #132). The three bands are written down as a promise, and the queue is an overlay when `available − panelWidth < 480` rather than at a breakpoint, because the panel's width is user state. - **#69** — `page-header` overflow (PR #136). Needed an *actions API* first, not a rule added in one place: a component cannot move another component's light-DOM children into a dropdown and keep their behaviour. That is why it was its own PR rather than part of #24. So **Phase 2's first bullet is complete** and the next in the stated order is **#6** (global back/forward) → **#72**. One thing worth checking before anyone claims #6: the back *stack* may already exist. `index.ts` records each navigation with `pushState` and replays `popstate`, and the old `navStack` was deleted rather than kept beside it — that landed for Android's hardware back button. #6 asks for global back/**forward**, so it is plausibly now a smaller job than this list assumes, or already partly satisfied. Someone should read it against the code before estimating it. **Two Phase 3 items were taken out of order and are closed** (#61, #68, in PR #139's quick-wins batch). Both were small and independent, so no sequence was broken — but noting it here so the phase list is not read as still owing them. #55 became unblocked at #24 and is still open, with the overlay to build on. **Two new issues were filed against this backlog** and want sequencing: - **#137** — three more hover-only affordances (queue remove, cover-art edit, shortcut reset). The correct fix is the *opposite* of #68's: on touch they must be always visible, since hiding them removes the only route to the action. Sequenced after #60. - **#138** — `play-in-context.test.ts` flakes ~1 in 3 on pristine `main`. Not in any phase; it is infrastructure, and it can fail CI on an unrelated PR, which is how it was found.
Author
Collaborator

Phase 2's remaining pair should be worked in the opposite order: #72
before #6.

Not a quiet reorder — this list is the sequence, and picking by eye is
how it stops meaning anything. So the case, and the measurements behind
it, for the record.

The list says #6 global back/forward, then #72 (nav highlight, Android, same mechanism). "Same mechanism" is exactly right and is the
reason to invert it.

#72 is not a bug in the nav components; it is a missing broadcast in
the shell.
handleNavigate() sets data-active-view on every path
including _isBack, but the navs learn the active view from the
navigate CustomEvent, which only the outbound path dispatches —
popstate calls handleNavigate directly. #6 adds a forward
control, which is a second route into that same broken state. Fixing
the broadcast first means #6 lands on a foundation that already works;
doing #6 first means shipping an affordance whose visible effect is
wrong from the first press.

And #72 is worse than its title. Measured against
make dev-headless SEED=default, reading the lit item out of each
nav's shadow root:

steps data-active-view sidebar lit bottom-nav lit
Albums (390px) albums nav-albums tab-albums
→ open an album explore-album-details nav-albums (nothing)
→ back albums nav-albums (nothing)
Albums → Tracks (1280px) tracks nav-tracks tab-tracks
→ back (lands on Albums) albums nav-tracks tab-tracks

The last row is desktop, and both navs highlight the view the user just
left. It is not Android-only, and a confidently wrong highlight is
worse than an absent one — any back across two primary views produces
it. Priorities agree with the inversion too: #72 is Priority/High +
Kind/Bug, #6 is Priority/Medium + Kind/Feature.

Nothing is lost by the swap. #6's own groundwork stays where it is
pushedEntries is already the depth counter its Direction asks for —
and #72's fix hands #6 something it would otherwise have had to build:
one place that states which view is active, which is what a forward
button has to keep honest.

#72 is done (PR #141). The order for the rest of Phase 2 is unchanged;
#6 is next in it.

**Phase 2's remaining pair should be worked in the opposite order: #72 before #6.** Not a quiet reorder — this list is the sequence, and picking by eye is how it stops meaning anything. So the case, and the measurements behind it, for the record. The list says `#6 global back/forward, then #72 (nav highlight, Android, same mechanism)`. "Same mechanism" is exactly right and is the reason to invert it. **#72 is not a bug in the nav components; it is a missing broadcast in the shell.** `handleNavigate()` sets `data-active-view` on every path including `_isBack`, but the navs learn the active view from the `navigate` CustomEvent, which only the *outbound* path dispatches — `popstate` calls `handleNavigate` directly. #6 adds a **forward** control, which is a second route into that same broken state. Fixing the broadcast first means #6 lands on a foundation that already works; doing #6 first means shipping an affordance whose visible effect is wrong from the first press. **And #72 is worse than its title.** Measured against `make dev-headless SEED=default`, reading the lit item out of each nav's shadow root: | steps | `data-active-view` | sidebar lit | bottom-nav lit | |---|---|---|---| | Albums (390px) | `albums` | `nav-albums` | `tab-albums` | | → open an album | `explore-album-details` | `nav-albums` | **(nothing)** | | → back | `albums` | `nav-albums` | **(nothing)** | | Albums → Tracks (1280px) | `tracks` | `nav-tracks` | `tab-tracks` | | → back (lands on Albums) | `albums` | **`nav-tracks`** | **`tab-tracks`** | The last row is desktop, and both navs highlight the view the user just *left*. It is not Android-only, and a confidently wrong highlight is worse than an absent one — any back across two primary views produces it. Priorities agree with the inversion too: #72 is `Priority/High` + `Kind/Bug`, #6 is `Priority/Medium` + `Kind/Feature`. **Nothing is lost by the swap.** #6's own groundwork stays where it is — `pushedEntries` is already the depth counter its Direction asks for — and #72's fix hands #6 something it would otherwise have had to build: one place that states which view is active, which is what a forward button has to keep honest. #72 is done (PR #141). The order for the rest of Phase 2 is unchanged; #6 is next in it.
Author
Collaborator

Phase 2's back/forward pair is done, in the inverted order argued
above.
#72 (PR #141) then #6 (PR #144), and the inversion paid for
itself: #6's Back button is the first thing in the app that had to
state whether going back was possible, and that disabled state
immediately exposed #142 — the app records two launch navigations, so
a fresh session was one entry deep, the first back press replayed home
over home, and on Android the press that should have exited the app
was swallowed by canGoBack(). Five phases old, invisible until
something asked the question out loud.

Phase 2's remaining items are unchanged, and the next by this list is
#25 (configurable sidebar tabs), which unlocks #27 and relates to
#37.

Two things measured while doing #6 that touch other items on this list:

  • #143 (new) — the top bar is 611px inside a 600px viewport, i.e.
    it already overflows at the bottom of the Compact band #24 committed
    to. layout-overflow.spec.ts checks 900 because that is the
    narrowest content area; the narrowest header is a different
    question and lands at 600. Relates to #48 (top bar rework) and to
    #57, whose "search becomes a modal" would fix it as a side effect —
    worth doing together rather than twice.
  • #66 got the measurement for its width half: at 390px the album
    page's "Shuffle album" is clipped by 53px and "Add to queue" by 50px,
    both unreachable, with album-info squeezed to 78px. Note that #69's
    actions API does not reach it — that header is the component's own
    markup, not <page-header> — so it is layout work, not a rule.
**Phase 2's back/forward pair is done, in the inverted order argued above.** #72 (PR #141) then #6 (PR #144), and the inversion paid for itself: #6's Back button is the first thing in the app that had to state whether going back was *possible*, and that disabled state immediately exposed #142 — the app records two launch navigations, so a fresh session was one entry deep, the first back press replayed home over home, and on Android the press that should have exited the app was swallowed by `canGoBack()`. Five phases old, invisible until something asked the question out loud. Phase 2's remaining items are unchanged, and the next by this list is **#25** (configurable sidebar tabs), which unlocks #27 and relates to #37. Two things measured while doing #6 that touch other items on this list: - **#143** (new) — the top bar is 611px inside a 600px viewport, i.e. it already overflows at the bottom of the Compact band #24 committed to. `layout-overflow.spec.ts` checks 900 because that is the narrowest *content area*; the narrowest *header* is a different question and lands at 600. Relates to #48 (top bar rework) and to #57, whose "search becomes a modal" would fix it as a side effect — worth doing together rather than twice. - **#66** got the measurement for its width half: at 390px the album page's "Shuffle album" is clipped by 53px and "Add to queue" by 50px, both unreachable, with `album-info` squeezed to 78px. Note that #69's actions API does not reach it — that header is the component's own markup, not `<page-header>` — so it is layout work, not a rule.
Author
Collaborator

Phase 2's next item is #25, and its stated order with #27 holds — but
only because of a design choice, so here is the choice.

The list says #25 configurable sidebar tabs → unlocks #27 (Jobs into Settings). The obvious objection is that it is backwards: #27 deletes
a destination from the very list #25 is making configurable, so #25
ships a toggle for something about to stop existing, and somebody has to
migrate that key out of config.toml later.

That objection is real against the shape you would reach for first —
HiddenViews []string, or a struct with one boolean per view. Both make
a removed view into stored garbage: the list carries a "jobs" string
that now matches nothing, and the struct carries a ShowJobs field that
has to be deprecated in place or removed with a migration.

It does not bite against a map keyed by view id, which is what I am
building. An absent key means "that view's own default"; an unknown key
is dropped on load. So when #27 removes Jobs, jobs = true sitting in
someone's config is not a migration — it is a key nothing asks about,
discarded the next time the file is written. The same property is what
makes the forward direction free: a view added in a later issue gets
its own default rather than being invisible (a whitelist) or forcibly
visible (a blacklist) to every existing install.

So: order unchanged, #25 then #27, and the reason the order is safe
is written into the storage shape rather than into anybody's memory. If
a future issue swaps that map for a list or a struct, it re-opens this
question and should say so.

One thing the sequence does buy, which is worth stating because it is
the actual dependency: #27's Direction says "removing the tab pairs with
the sidebar-toggles issue", and with #25 landed, #27 does not have to
argue about what happens to a user who wants the Jobs tab. It can
delete the destination outright, because by then the answer to "I liked
that tab" is a toggle list that no longer offers it — a visible,
explicable absence rather than a feature that silently vanished.

Also, one finding in #25 has expired and I am not repeating it. Its
fourth bullet says the sidebar's item count is one of the two reasons
the window minimum is 600px tall. Both reasons were retired by #24
backend/config/window.go records that the subtitle is display: none
from 899px down and that the sidebar host scrolls (overflow-y: auto;
at 600x460 its scrollHeight is 434 against a 332px client). 800x600 is
a comfort floor for desktop chrome now, not a correctness one, so
hiding tabs relieves nothing there.

**Phase 2's next item is #25, and its stated order with #27 holds — but only because of a design choice, so here is the choice.** The list says `#25 configurable sidebar tabs → unlocks #27 (Jobs into Settings)`. The obvious objection is that it is backwards: #27 *deletes* a destination from the very list #25 is making configurable, so #25 ships a toggle for something about to stop existing, and somebody has to migrate that key out of `config.toml` later. That objection is real against the shape you would reach for first — `HiddenViews []string`, or a struct with one boolean per view. Both make a removed view into stored garbage: the list carries a `"jobs"` string that now matches nothing, and the struct carries a `ShowJobs` field that has to be deprecated in place or removed with a migration. It does not bite against a **map keyed by view id**, which is what I am building. An absent key means "that view's own default"; an unknown key is dropped on load. So when #27 removes Jobs, `jobs = true` sitting in someone's config is not a migration — it is a key nothing asks about, discarded the next time the file is written. The same property is what makes the *forward* direction free: a view added in a later issue gets its own default rather than being invisible (a whitelist) or forcibly visible (a blacklist) to every existing install. So: **order unchanged, #25 then #27**, and the reason the order is safe is written into the storage shape rather than into anybody's memory. If a future issue swaps that map for a list or a struct, it re-opens this question and should say so. One thing the sequence does buy, which is worth stating because it is the actual dependency: #27's Direction says "removing the tab pairs with the sidebar-toggles issue", and with #25 landed, #27 does not have to argue about what happens to a user who *wants* the Jobs tab. It can delete the destination outright, because by then the answer to "I liked that tab" is a toggle list that no longer offers it — a visible, explicable absence rather than a feature that silently vanished. **Also, one finding in #25 has expired and I am not repeating it.** Its fourth bullet says the sidebar's item count is one of the two reasons the window minimum is 600px tall. Both reasons were retired by #24 — `backend/config/window.go` records that the subtitle is `display: none` from 899px down and that the sidebar host scrolls (`overflow-y: auto`; at 600x460 its `scrollHeight` is 434 against a 332px client). 800x600 is a comfort floor for desktop chrome now, not a correctness one, so hiding tabs relieves nothing there.
Author
Collaborator

#25 landed (PR #145, merged as 5af545e), so Phase 2's next item is
#27 — and the storage shape argued above is what it should lean on:
removing the Jobs destination costs nothing, because jobs = true in an
existing config is an unknown key, dropped on load.

One thing #27 inherits: backend/config.Views is now also what
DefaultPage validates against, so deleting Jobs is one entry there
rather than an entry plus a switch arm plus a <select> option.

**#25 landed** (PR #145, merged as `5af545e`), so Phase 2's next item is **#27** — and the storage shape argued above is what it should lean on: removing the Jobs destination costs nothing, because `jobs = true` in an existing config is an unknown key, dropped on load. One thing #27 inherits: `backend/config.Views` is now also what `DefaultPage` validates against, so deleting Jobs is one entry there rather than an entry plus a `switch` arm plus a `<select>` option.
Author
Collaborator

#27 landed (PR #147, merged as the tip of main), so Phase 2 has
#48 (top bar), #23/#42 (transport, volume) and #30 (track list
chrome) left, plus the newly-filed #143 — the top bar overflows by
11px at 600x600, which is inside the Compact band #24 promised and is
not on this list because it was filed after it.

#143 relates to #48 and to #57 (whose "search becomes a modal" would
fix it as a side effect), so the three are one pass over one component
rather than three.

One thing #27 turned up that is worth recording against the sequence:
the Direction said "split by kind", and four of the five kinds already
had a home showing their work. The tab was carrying the generic job
affordances — pause, cancel, the log — for surfaces that each had their
own progress display and none of those. Worth assuming the same of #62
(jobs as an Android notification): the question there is likely also
"what does the indicator carry that a notification would not", rather
than a straight port.

**#27 landed** (PR #147, merged as the tip of `main`), so Phase 2 has `#48` (top bar), `#23`/`#42` (transport, volume) and `#30` (track list chrome) left, plus the newly-filed **#143** — the top bar overflows by 11px at 600x600, which is inside the Compact band #24 promised and is not on this list because it was filed after it. #143 relates to #48 and to #57 (whose "search becomes a modal" would fix it as a side effect), so the three are one pass over one component rather than three. One thing #27 turned up that is worth recording against the sequence: the Direction said "split by kind", and four of the five kinds already had a home showing their work. The tab was carrying the *generic* job affordances — pause, cancel, the log — for surfaces that each had their own progress display and none of those. Worth assuming the same of #62 (jobs as an Android notification): the question there is likely also "what does the indicator carry that a notification would not", rather than a straight port.
Author
Collaborator

#143 and #43 landed (PRs #149 and #152). Two notes against the
sequence, one of which is a correction to this list.

#43 was not in any phase, only in the soft-relations line about #5,
#43 and #63 being three answers to "how does selection work in a list".
It was also the last Priority/High desktop item — everything else
at High is Android now. Worth placing rather than leaving to be found:
it belongs beside #5, and #5 should be read before it is started, since
the finding below lands on it.

The finding. explore-link stops the click's propagation on
purpose, so a click on any track/album/artist name navigates and
selects nothing. That is app-wide and it means adding
SelectionController to a list does not give that list selection on its
names. Measured by hit-scanning a row: the queue panel is 12% link,
track-list is 21%. explore-album-details's tracklist is
number/title/artist/duration and is the one that plausibly is mostly
link — so #5 should scan that row before copying the pattern, and if the
number is bad, the answer is #67's (names into the context menu) rather
than a new one.

Phase 2 now has #48, #23/#42 and #30 left. #143 leaves #48 something
to build on rather than something to undo: the top bar's fit is
measured (services/top-bar-fit.ts), so a three-column header inherits
a working answer to "what does this bar drop when it runs out of room"
and only has to declare the new order. The two things it drops are the
wordmark and the job indicator's label — note that #48's own plan is to
replace the wordmark with "YJ", which makes the first of those cheaper
still.

And #62 keeps the property #27 turned up. #148, filed off #143, is
the second thing the Android top bar has nowhere to put: the library
filter is hidden below 600px and nothing else in the app selects a
library
. So #57's blocker list is really "where do the job indicator
and the library filter live", and #62 answers only the first. Worth
knowing before #62 is scoped as a straight port.

**#143 and #43 landed** (PRs #149 and #152). Two notes against the sequence, one of which is a correction to this list. **#43 was not in any phase**, only in the soft-relations line about #5, #43 and #63 being three answers to "how does selection work in a list". It was also the last `Priority/High` **desktop** item — everything else at High is Android now. Worth placing rather than leaving to be found: it belongs beside #5, and #5 should be read before it is started, since the finding below lands on it. **The finding.** `explore-link` stops the click's propagation on purpose, so a click on any track/album/artist *name* navigates and selects nothing. That is app-wide and it means adding `SelectionController` to a list does not give that list selection on its names. Measured by hit-scanning a row: the queue panel is **12%** link, `track-list` is **21%**. `explore-album-details`'s tracklist is number/title/artist/duration and is the one that plausibly *is* mostly link — so #5 should scan that row before copying the pattern, and if the number is bad, the answer is #67's (names into the context menu) rather than a new one. **Phase 2 now has #48, #23/#42 and #30 left.** #143 leaves #48 something to build on rather than something to undo: the top bar's fit is *measured* (`services/top-bar-fit.ts`), so a three-column header inherits a working answer to "what does this bar drop when it runs out of room" and only has to declare the new order. The two things it drops are the wordmark and the job indicator's label — note that #48's own plan is to replace the wordmark with "YJ", which makes the first of those cheaper still. **And #62 keeps the property #27 turned up.** #148, filed off #143, is the second thing the Android top bar has nowhere to put: the library filter is hidden below 600px and **nothing else in the app selects a library**. So #57's blocker list is really "where do the job indicator *and* the library filter live", and #62 answers only the first. Worth knowing before #62 is scoped as a straight port.
Author
Collaborator

Phase 3's second item is done. #57 (PR #167, merged as 880adff),
which also closed #148 — the library filter was the second awkward
thing in that bar and #148 says so in as many words.

The bar is out of the phone's grid template rather than hidden: 3.25em
of a 439 CSS px viewport, which is the largest single vertical win the
reference device has. The h1 survives visually-hidden, because
page-header renders no h1 when its heading is empty and Settings has
no page-header at all.

Two things about the sequence, for the record.

#60 stays in Phase 4 and this did not need it. The list's ordering
of these two was worth re-checking, because #57 builds a modal and #60
establishes "the presentation every other item needs" — but they are
different shapes under a rule already written down: a dialog that only
asks a question is a confirmAction() call; a dialog carrying input is
a <wa-dialog> in the host's own template
. <action-sheet> replaces
the six menus; a search box is not one, and nothing in #57 needs
undoing when it lands.

What #57 did take from #60 is the reason it is a dialog at all, and
it is worth naming because it is a constraint on everything Phase 4
touches: on the reference device wa-popup falls back to
position: fixed, and .main-panel's contain: paint clips a
fixed-position descendant. No tier here can reproduce that — CI's
Chromium and WebKit both have the Popover API — so a guard has to assert
the mechanism (a native <dialog> in the tree) rather than the
symptom. Phase 4's whole first item is that finding applied six times.

Phase 3's next item is #55 (queue as a screen), then #59 and #61.

One thing filed on the way: #168, e2e state leaking between spec
files — a job staged by top-bar-fit.spec.ts was still in the store
when a later spec measured a layout, so the leaking spec passed and an
unrelated one failed, in file order only.

**Phase 3's second item is done.** #57 (PR #167, merged as `880adff`), which also closed **#148** — the library filter was the second awkward thing in that bar and #148 says so in as many words. The bar is out of the phone's grid template rather than hidden: 3.25em of a 439 CSS px viewport, which is the largest single vertical win the reference device has. The `h1` survives visually-hidden, because `page-header` renders no `h1` when its heading is empty and Settings has no `page-header` at all. **Two things about the sequence, for the record.** **#60 stays in Phase 4 and this did not need it.** The list's ordering of these two was worth re-checking, because #57 builds a modal and #60 establishes "the presentation every other item needs" — but they are different shapes under a rule already written down: *a dialog that only asks a question is a `confirmAction()` call; a dialog carrying input is a `<wa-dialog>` in the host's own template*. `<action-sheet>` replaces the six **menus**; a search box is not one, and nothing in #57 needs undoing when it lands. **What #57 did take from #60 is the reason it is a dialog at all**, and it is worth naming because it is a constraint on everything Phase 4 touches: on the reference device `wa-popup` falls back to `position: fixed`, and `.main-panel`'s `contain: paint` clips a fixed-position descendant. **No tier here can reproduce that** — CI's Chromium and WebKit both have the Popover API — so a guard has to assert the *mechanism* (a native `<dialog>` in the tree) rather than the symptom. Phase 4's whole first item is that finding applied six times. **Phase 3's next item is #55** (queue as a screen), then #59 and #61. One thing filed on the way: **#168**, e2e state leaking between spec files — a job staged by `top-bar-fit.spec.ts` was still in the store when a later spec measured a layout, so the leaking spec passed and an unrelated one failed, in file order only.
Author
Collaborator

Phase 3, on the #60-before-#55 question — raised at the end of the
#57 session and left for whoever picked up #55. Taking #55 now, and the
answer is that the ordering constraint does not exist, rather than that
it is being ignored.

The recommendation was sound as stated: #55's Direction asks for a
DETAIL_LOADERS mount, .main-panel > * is contain: layout style paint under a .main-panel that is contain: content, and #60's
finding is that position: fixed escapes overflow but not paint
containment on Chrome 113. So #55-as-specified would move
queue-panel's working wa-popup context menu into the region where
#60 exists — a regression no tier here can see.

#55 is not being built that way, and the reason is a measurement rather
than a preference: at 424x439 the overlay #24 shipped is already
424x318, which is exactly .main-panel's rect. A detail-view mount
draws the same rectangle in the same place. What #55 is actually
missing is the back stack (back navigates the page underneath and
leaves the queue up — measured) and a reachable way out (the scrim has
zero uncovered pixels at phone width). Both are had without moving the
component, so the menu stays where it already works.

Full argument on #55. No change to the roadmap order is being
proposed
#60 keeps its place and its priority, it simply stops
being a precondition for #55. It still blocks #63/#67/#71 and is still
the highest-value unclaimed item in this phase once the queue chain is
done.

**Phase 3, on the #60-before-#55 question** — raised at the end of the #57 session and left for whoever picked up #55. Taking #55 now, and the answer is that the ordering constraint does not exist, rather than that it is being ignored. The recommendation was sound as stated: #55's Direction asks for a `DETAIL_LOADERS` mount, `.main-panel > *` is `contain: layout style paint` under a `.main-panel` that is `contain: content`, and #60's finding is that `position: fixed` escapes overflow but not paint containment on Chrome 113. So #55-as-specified would move `queue-panel`'s working `wa-popup` context menu into the region where #60 exists — a regression no tier here can see. #55 is not being built that way, and the reason is a measurement rather than a preference: at 424x439 the overlay #24 shipped is *already* 424x318, which is exactly `.main-panel`'s rect. A detail-view mount draws the same rectangle in the same place. What #55 is actually missing is the back stack (back navigates the page underneath and leaves the queue up — measured) and a reachable way out (the scrim has zero uncovered pixels at phone width). Both are had without moving the component, so the menu stays where it already works. Full argument on #55. **No change to the roadmap order is being proposed** — #60 keeps its place and its priority, it simply stops being a precondition for #55. It still blocks #63/#67/#71 and is still the highest-value unclaimed item in this phase once the queue chain is done.
Author
Collaborator

Phase 3 items 3 and 4: proposing #59 and #56 as one PR, argued here
rather than done quietly. #55 has merged, so #59 is unblocked and is
next in the stated order; this only asks to pull item 4 forward into the
same change, not to reorder anything.

They are the same component and the same row of pixels. #59 removes
controls from the phone's transport; #56 enlarges what is left of it.
Both issues already say so in their own text:

  • #56's Findings: "four 48px targets plus the art and metadata is
    already tight — which is the argument for the separate issue
    slimming the mini player
    ".
  • #56's Direction: "Sequence with the mini-player-slimming and
    small-screen issues."
  • #59's Direction: "with the larger targets from the control-size
    issue".

Done separately, the bar gets laid out twice and measured twice, and the
first PR ships a state neither issue wants — either three controls still
at 25px, or five controls at 48px in 424 CSS px, which is the overflow
#56 measured. The intermediate state is not a smaller version of the
goal; it is a different, worse layout.

One component decides both. player-controls renders all five
buttons unconditionally and is composed by audio-player (the bar) and
by now-playing-view. Neither issue can be done without giving it a
context, and once it has one, both are the same edit:

context controls size
bar, desktop five, as today as today
bar, phone play/pause, prev/next, favourite large play/pause
Now Playing five larger still, secondary below

And the context cannot be a media query alone, which is the part
worth pinning: on a phone the bar wants three controls and Now Playing
wants five larger ones, at the same viewport. So the host says which
context and the viewport says which size band, and neither alone is
enough — #56's Finding already says this ("sizing must be per-context, a
size property or a host-set custom property, not a global bump"), and it
is the exception to the rule that a component states what it drops at
phone width itself.

What must not break is the matrix's promise from plan 018: no
action is ever unreachable at any supported size
. Shuffle, repeat and
the queue leave the phone's bar and must still be reachable — they are,
on Now Playing, which the mini player's art opens (.expand, phone
only) and which #55 just gave the queue a back stack from. That
reachability is the acceptance criterion, not the pixel count.

If there is disagreement, say so and I will split them; the argument is
about laying one bar out once, not about batching for its own sake.

**Phase 3 items 3 and 4: proposing #59 and #56 as one PR**, argued here rather than done quietly. #55 has merged, so #59 is unblocked and is next in the stated order; this only asks to pull item 4 forward into the same change, not to reorder anything. **They are the same component and the same row of pixels.** #59 removes controls from the phone's transport; #56 enlarges what is left of it. Both issues already say so in their own text: - #56's Findings: "four 48px targets plus the art and metadata is already tight — **which is the argument for the separate issue slimming the mini player**". - #56's Direction: "Sequence with the mini-player-slimming and small-screen issues." - #59's Direction: "with the larger targets from the control-size issue". Done separately, the bar gets laid out twice and measured twice, and the first PR ships a state neither issue wants — either three controls still at 25px, or five controls at 48px in 424 CSS px, which is the overflow #56 measured. The intermediate state is not a smaller version of the goal; it is a different, worse layout. **One component decides both.** `player-controls` renders all five buttons unconditionally and is composed by `audio-player` (the bar) and by `now-playing-view`. Neither issue can be done without giving it a context, and once it has one, both are the same edit: | context | controls | size | |---|---|---| | bar, desktop | five, as today | as today | | bar, phone | play/pause, prev/next, favourite | large play/pause | | Now Playing | five | larger still, secondary below | **And the context cannot be a media query alone**, which is the part worth pinning: on a *phone* the bar wants three controls and Now Playing wants five larger ones, at the same viewport. So the host says which context and the viewport says which size band, and neither alone is enough — #56's Finding already says this ("sizing must be per-context, a size property or a host-set custom property, not a global bump"), and it is the exception to the rule that a component states what it drops at phone width itself. **What must not break** is the matrix's promise from plan 018: *no action is ever unreachable at any supported size*. Shuffle, repeat and the queue leave the phone's bar and must still be reachable — they are, on Now Playing, which the mini player's art opens (`.expand`, phone only) and which #55 just gave the queue a back stack from. That reachability is the acceptance criterion, not the pixel count. If there is disagreement, say so and I will split them; the argument is about laying one bar out once, not about batching for its own sake.
Author
Collaborator

Taking #64 (Phase 4, "independent") ahead of Phase 3's #58 and #51.
Recording the argument rather than doing it silently.

#51 is the phase's verification pass — this issue says to do it last
"since each buys vertical space". #172 is what that verification will be
run against, and it measures Now Playing at the reference device's
424x439 after #56:

element height
header 48
album art 39
title / artist / album ~66
seek bar ~30
transport (controls + secondary row + volume) 172

#64 is the only unspent vertical win in the backlog that is pure
gain
— ~30px including its gap — and #172's own Direction says to do
it first and re-measure. Everything else on that screen is a trade:
#172 already considered and rejected folding shuffle and repeat back
onto the primary row (buys 52px, leaves the art at 91px, costs a third
arrangement of the same five buttons). So verifying #51 before spending
the free 30px is verifying the wrong build, and #58 adds a progress
line
to the same screen — it spends vertical space rather than
banking it, and is better judged once the budget is settled.

The ordering cost is small: #64 has no dependencies, is not blocked, and
blocks nothing. #58 and #51 keep their order behind it.

Two things I considered instead and did not take. #60 is the
highest-leverage unclaimed item in the tracker (Priority/High,
Reviewed/Confirmed, blocks #63/#67/#71) and is the containment defect
#55 worked around rather than fixed — it is the one I would take with
a second session, and it opens Phase 4 properly. #58 is the strict
roadmap next. Both remain available; this is a one-issue reorder inside
the same screen, not a phase swap.

**Taking #64 (Phase 4, "independent") ahead of Phase 3's #58 and #51. Recording the argument rather than doing it silently.** #51 is the phase's *verification* pass — this issue says to do it last "since each buys vertical space". #172 is what that verification will be run against, and it measures Now Playing at the reference device's 424x439 after #56: | element | height | |---|---| | header | 48 | | **album art** | **39** | | title / artist / album | ~66 | | seek bar | ~30 | | transport (controls + secondary row + volume) | **172** | **#64 is the only unspent vertical win in the backlog that is pure gain** — ~30px including its gap — and #172's own Direction says to do it first and re-measure. Everything else on that screen is a trade: #172 already considered and rejected folding shuffle and repeat back onto the primary row (buys 52px, leaves the art at 91px, costs a third arrangement of the same five buttons). So verifying #51 before spending the free 30px is verifying the wrong build, and #58 adds a *progress line* to the same screen — it spends vertical space rather than banking it, and is better judged once the budget is settled. The ordering cost is small: #64 has no dependencies, is not blocked, and blocks nothing. #58 and #51 keep their order behind it. Two things I considered instead and did not take. **#60** is the highest-leverage unclaimed item in the tracker (`Priority/High`, `Reviewed/Confirmed`, blocks #63/#67/#71) and is the containment defect #55 worked *around* rather than fixed — it is the one I would take with a second session, and it opens Phase 4 properly. **#58** is the strict roadmap next. Both remain available; this is a one-issue reorder inside the same screen, not a phase swap.
Author
Collaborator

Phase 3 is done. #51 was its last open item and is in PR #188; #62,
#57, #55, #59, #61, #56, #58 and #52 were already closed.

The sequencing this list insisted on paid for itself, so it is worth
recording how. #51 says to verify the umbrella "after the above,
since each buys vertical space", and that turned out to understate it:
the audit's result is that there is no width problem left at
424x439
— the shell does not overflow on any of the ten primary views
plus the queue, nothing is stranded, nothing is occluded — so the whole
of #51 reduced to the vertical axis, which is exactly what its own
Findings predicted and what none of the earlier items could have shown.
Auditing before them would have measured a build nobody will ship.

Two findings came out of it and are filed as #186 (every control
that is not the transport is under the 44px touch floor) and #187
(the seek bar's drag target is 6px). Both are Priority/High and neither
is a reachability failure.

Where they sit in this list is a question rather than an
assumption.
#186 is Phase 4's subject matter — it is "the polish pass
over the same surfaces" that #54 and #68 already describe — but it is
also the single most-repeated defect in the app right now, on every
screen, and it is the same complaint #56 was filed for. My reading is
that it belongs at the head of Phase 4, before #63, on the grounds
that #63 adds gestures to lists whose controls are 21px, and that
sizing them first makes the gesture work easier to judge rather than
harder. #187 is small and independent and can go with it.

Not doing that unilaterally, since this list is the sequence. Phase 4's
stated order is #63, then #67 and #71, with #54 and #68 as the polish
pass — so unless someone disagrees, the next item is #63, and #186
is the one I would argue should displace it.

One correction to the record while I am here: this list has #64 under
Phase 4's "independent" group, and it turned out to be a hard
prerequisite for #172 and therefore for #51 — it is the 29px that made
the reflow's arithmetic work. It is closed, so nothing needs
reordering; it is only that "independent" was wrong about it.

**Phase 3 is done.** #51 was its last open item and is in PR #188; #62, #57, #55, #59, #61, #56, #58 and #52 were already closed. The sequencing this list insisted on paid for itself, so it is worth recording *how*. #51 says to verify the umbrella "after the above, since each buys vertical space", and that turned out to understate it: the audit's result is that there is **no width problem left at 424x439** — the shell does not overflow on any of the ten primary views plus the queue, nothing is stranded, nothing is occluded — so the whole of #51 reduced to the vertical axis, which is exactly what its own Findings predicted and what none of the earlier items could have shown. Auditing before them would have measured a build nobody will ship. Two findings came out of it and are filed as **#186** (every control that is not the transport is under the 44px touch floor) and **#187** (the seek bar's drag target is 6px). Both are Priority/High and neither is a reachability failure. **Where they sit in this list is a question rather than an assumption.** #186 is Phase 4's subject matter — it is "the polish pass over the same surfaces" that #54 and #68 already describe — but it is also the single most-repeated defect in the app right now, on every screen, and it is the same complaint #56 was filed for. My reading is that it belongs **at the head of Phase 4, before #63**, on the grounds that #63 adds gestures to lists whose controls are 21px, and that sizing them first makes the gesture work easier to judge rather than harder. #187 is small and independent and can go with it. Not doing that unilaterally, since this list is the sequence. Phase 4's stated order is #63, then #67 and #71, with #54 and #68 as the polish pass — so unless someone disagrees, the next item is **#63**, and #186 is the one I would argue should displace it. One correction to the record while I am here: this list has #64 under Phase 4's "independent" group, and it turned out to be a hard prerequisite for #172 and therefore for #51 — it is the 29px that made the reflow's arithmetic work. It is closed, so nothing needs reordering; it is only that "independent" was wrong about it.
Author
Collaborator

Taking #160 next, ahead of #186 and ahead of #135. I recommended #186
earlier today and am changing that recommendation; the argument belongs
here rather than in a commit message.

Phase 3 closed with #51, so the next open work is Phase 4 plus the three
Priority/High Android issues filed since this list was written — #135
(static popping), #160 (slog goes nowhere), #186 (touch targets). #186 is
the one I named this morning, on the grounds that it is measured,
confirmed and mechanical. All three of those are still true. It is still
not first.

#135 is the highest-value of the three and #160 is inside it. #135's
Direction opens with "Count the underruns and log them", and its Findings
say why that is the cheapest first move: an underrun makes
BufferedStreamer.Stream zero the caller's buffer and return ok, and a
run of zeros spliced into a waveform is a step discontinuity at both
edges — which is what a pop is, and a series of short ones is static.
That is the one candidate of three whose audible signature matches the
report, and starved/starvedSince already exist from #122 but feed
only a 3-second give-up threshold, so a hundred 20ms underruns a minute
are invisible to the log, the UI and every tier.

Then #160: every slog line on Android is discarded. So #135's own
recommended first step is unreadable on the device that has the bug. Add
a counter, add a log line, run the phone, read nothing. That is the
sequence to avoid, and the ordering constraint is not a preference —
"instrument, then listen" cannot be executed at all until the
instrument has an output.

#160 also pays back permanently, and there is a measured precedent for
what it costs not to have it.
#52 sat at Priority/Critical for
months as a process that vanished with no tombstone. Its entire
diagnosis is one line main.go already writes —
sLogger.Error("application error", "err", err.Error()) — where err
named the fault exactly. It went to /dev/null. Every future Android
bug worked from this repo is diagnosed against that same silence until
#160 lands, which is the argument for it being first rather than
merely soon.

What this costs #186 is a session, and nothing else. It is not
blocked by either, shares no surface with either, and its finding — a
device measurement table over 18 controls — does not go stale. Phase 4's
#63/#67/#71 are likewise untouched: this reorders the three post-#73
High issues among themselves and leaves the phase list alone.

So: #160, then #135, then #186, then Phase 4 proper.

**Taking #160 next, ahead of #186 and ahead of #135. I recommended #186 earlier today and am changing that recommendation; the argument belongs here rather than in a commit message.** Phase 3 closed with #51, so the next open work is Phase 4 plus the three `Priority/High` Android issues filed since this list was written — #135 (static popping), #160 (slog goes nowhere), #186 (touch targets). #186 is the one I named this morning, on the grounds that it is measured, confirmed and mechanical. All three of those are still true. It is still not first. **#135 is the highest-value of the three and #160 is inside it.** #135's Direction opens with "Count the underruns and log them", and its Findings say why that is the cheapest first move: an underrun makes `BufferedStreamer.Stream` zero the caller's buffer and return `ok`, and a run of zeros spliced into a waveform is a step discontinuity at both edges — which is what a pop *is*, and a series of short ones is static. That is the one candidate of three whose audible signature matches the report, and `starved`/`starvedSince` already exist from #122 but feed only a 3-second give-up threshold, so a hundred 20ms underruns a minute are invisible to the log, the UI and every tier. Then #160: **every `slog` line on Android is discarded.** So #135's own recommended first step is unreadable on the device that has the bug. Add a counter, add a log line, run the phone, read nothing. That is the sequence to avoid, and the ordering constraint is not a preference — "instrument, then listen" cannot be executed at all until the instrument has an output. **#160 also pays back permanently, and there is a measured precedent for what it costs not to have it.** #52 sat at `Priority/Critical` for months as a process that vanished with no tombstone. Its entire diagnosis is one line `main.go` already writes — `sLogger.Error("application error", "err", err.Error())` — where `err` named the fault exactly. It went to `/dev/null`. Every future Android bug worked from this repo is diagnosed against that same silence until #160 lands, which is the argument for it being first rather than merely soon. **What this costs #186 is a session, and nothing else.** It is not blocked by either, shares no surface with either, and its finding — a device measurement table over 18 controls — does not go stale. Phase 4's #63/#67/#71 are likewise untouched: this reorders the three post-#73 High issues among themselves and leaves the phase list alone. So: **#160, then #135, then #186**, then Phase 4 proper.
Author
Collaborator

#160 is done (PR #191, merged as 25ea1f3), and the reorder argued
above paid off in a way worth recording against the sequence, because
it changes what the next person should do.

#160 justified itself inside the first minute. The app's diagnostic
stream reached logcat for the first time and immediately produced two
bugs that have been written on every launch and discarded — #189 (a
backfill querying release_groups, a table plan 013 renamed away, so
it has failed on its first statement since e7748f1) and #190 (SQLite
has no temp directory on Android, so the champion index never builds
and every Explore search on the platform takes the slow path). Neither
is Android-only in origin and #189 is not Android-only at all; they
were simply unread. That is the argument for this class of work
generalised: a platform nobody can read is a platform where every bug
costs what #52 cost.

#135 is now an instrument and a redirection, not a fix. The
Findings ranked the ring-buffer underrun first, on the sound reasoning
that its audible signature matches the report. Measured on the device
it is zero — steady playback, playback under a full index build,
and playback across six seeks, all zero, with the instrument proved
live at 1 Hz by a temporary unconditional build. This issue's own
Direction says what follows: candidates 1 and 3 are out.

Candidate 2 gained hardware evidence in the same pass: dumpsys audio
reports our own stream as OpenSL ES AudioPlayer (Buffer Queue) ... sampleRate=48000 while speakerSampleRate is hardcoded to 44100. So
the remaining work on #135 is the device-rate one, and it now has a
measurement to be judged against rather than three plausible guesses.
#135 is unclaimed and its comment carries the table.

On the sequence itself, nothing else moves. #186 and #187 are next
at Priority/High, then Phase 4 proper (#63, #67, #71, with #54 and
#68 as the polish pass). #189 and #190 are new and both Priority/High
#190 is a silent performance cliff on the slowest device this app
runs on, and #189 is a feature that has not worked in either platform
for some time, so both have a claim on being taken before Phase 4.
Placing them is a judgement I am leaving rather than making.

**#160 is done** (PR #191, merged as `25ea1f3`), and the reorder argued above paid off in a way worth recording against the sequence, because it changes what the *next* person should do. **#160 justified itself inside the first minute.** The app's diagnostic stream reached logcat for the first time and immediately produced two bugs that have been written on every launch and discarded — #189 (a backfill querying `release_groups`, a table plan 013 renamed away, so it has failed on its first statement since `e7748f1`) and #190 (SQLite has no temp directory on Android, so the champion index never builds and every Explore search on the platform takes the slow path). Neither is Android-only in origin and #189 is not Android-only at all; they were simply unread. That is the argument for this class of work generalised: a platform nobody can read is a platform where every bug costs what #52 cost. **#135 is now an instrument and a redirection, not a fix.** The Findings ranked the ring-buffer underrun first, on the sound reasoning that its audible signature matches the report. Measured on the device it is **zero** — steady playback, playback under a full index build, and playback across six seeks, all zero, with the instrument proved live at 1 Hz by a temporary unconditional build. This issue's own Direction says what follows: candidates 1 and 3 are out. Candidate 2 gained hardware evidence in the same pass: `dumpsys audio` reports our own stream as `OpenSL ES AudioPlayer (Buffer Queue) ... sampleRate=48000` while `speakerSampleRate` is hardcoded to 44100. So the remaining work on #135 is the device-rate one, and it now has a measurement to be judged against rather than three plausible guesses. #135 is unclaimed and its comment carries the table. **On the sequence itself**, nothing else moves. #186 and #187 are next at `Priority/High`, then Phase 4 proper (#63, #67, #71, with #54 and #68 as the polish pass). #189 and #190 are new and both `Priority/High` — #190 is a silent performance cliff on the slowest device this app runs on, and #189 is a feature that has not worked in either platform for some time, so both have a claim on being taken before Phase 4. Placing them is a judgement I am leaving rather than making.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#73