android: the boot chain goes out in one round, and a census that holds it there

Thirty rounds of onMount had never been read end to end, and read end to end
it was a queue: two dozen commands with a critical path ten deep, because
Tauri's `listen` is an invoke like any other. loadLibrary asked for five
independent SQLite reads one behind the other, bindSlsk registered eight
listeners one behind the other, and watchLibrary and the player's bindEvents
did two each. None of them depended on anything before them. They go out in
one round now — the same commands, critical path 10 to 3, and ten of them
used to leave in the shell's own first turn where twenty-three do now — and
in a rig that runs the real store modules with the bridge instrumented,
lib.loaded lands at 53 ms instead of 85 at a 6 ms round trip and 62 instead
of 148 at 20 ms. Those are a desktop rig's milliseconds with the round trip
swept rather than measured; the phone's own numbers are unproven until the
walk prints bootMs and bootLibMs.

The behaviour is meant to be identical. loadLibrary's only movable part was
lib.error, which has two writers and used to be decided by the order they were
awaited in; the two messages are now carried out of the round and applied in
that same order, so a failing library still shows the library's message. Every
combination of which of the five reads fails, over six latency shapes, comes
out identical to the old code. bindSlsk still does not return until every
handler is up, so connectStored cannot let a fast connected state land with
nobody listening, and bindEvents still hands back the transport's unlisten and
still rejects only when the transport is the one that failed.

src/lib/boot.js is the project's first launch instrument — one counter every
invoke and every listen passes, sealed at the first frame the shell paints and
never re-opened, so a probe landing a second and a half later still reads the
launch. perf.mjs measures frames; nothing measured the launch. The mock walk's
Home step now holds a budget against it and both walks print bootCalls,
bootSync, bootMs and bootLibMs on every step. bootSync is the clause with no
clock in it: a call that waits on the one before it can only issue its first
in the shell's own turn, so a chain put back in a queue falls under the floor
whatever the machine is doing. A bootCalls of -2 means __ss.boot is not there
at all, which is the census itself being broken rather than the app.

One import line goes with it: Sheet.svelte's `import * as api`, the only one
of the ten dead imports this round turned up whose reason is the bundle at
all. Rollup already drops an unreferenced NAMED import, so a dead name on an
import line is hygiene rather than bytes; a namespace import is the one shape
tree-shaking cannot see through, and this one was dead. It frees nothing on
its own — twenty-nine other files in the phone's graph hold the same module
the same way, so api.js is pinned into the chunk whatever Sheet.svelte does —
but it is the only one of the ten that belongs to a round about weight. The
nine that are drift go out with the sweep in the round after this one.

Two things were looked at and left alone, both written down in ANDROID.md
rather than half-done. Home's per-artist catalog walk is the deepest chain in
the app, and that serial walk is the Deezer and MusicBrainz pacing, not an
oversight. And the follows really are read twice at launch, but the obvious
guard does not work: Home's effect calls loadHome with force the first time
they land, so the guard would never fire. One small local read, left for a
round that can do it properly.
This commit is contained in:
KeelrLeek committed 2026-09-14 01:38:15 -05:00
1 parent 052e15302b
commit 01bab007f7
10 files changed
+304 -43

No files matched your search

+47 -1
View File
@@ -251,6 +251,21 @@ const PROBE = `(function () {
halfSub: ((document.querySelector('.halfsheet .sheet-head .m') || {}).textContent || '').trim(),
halfSw: [].filter.call(document.querySelectorAll('.halfsheet .row'), function (r) { return !!r.querySelector('.sw.on'); }).map(function (r) { return ((r.querySelector('.t') || {}).textContent || '').trim(); }).join('|'),
contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),
/* the boot census, latched at the shell's first frame and never
re-opened: how many commands the app asked for before it had
drawn anything, how many of those left in the shell's own turn,
which kinds they were, and how long it took. A probe landing a
second and a half later still reads the launch. -1 is "the census
never sealed", -2 is "there is no census". bootCmds is the
distinct names, not the whole run of them — every step carries
it, and what is ever asked of it is whether a kind of command was
at boot at all. */
bootCalls: (function () { try { return __ss.boot.sealed ? __ss.boot.calls : -1; } catch (e) { return -2; } })(),
bootSync: (function () { try { return __ss.boot.sync; } catch (e) { return -2; } })(),
bootMs: (function () { try { return __ss.boot.frameMs == null ? -1 : __ss.boot.frameMs; } catch (e) { return -2; } })(),
bootLibMs: (function () { try { return __ss.boot.libMs == null ? -1 : __ss.boot.libMs; } catch (e) { return -2; } })(),
bootCmds: (function () { try { var c = __ss.boot.cmds, u = [], i; for (i = 0; i < c.length; i++) if (u.indexOf(c[i]) < 0) u.push(c[i]); return u.join(' '); } catch (e) { return ''; } })(),
navMs: (function () { try { var n = performance.getEntriesByType('navigation')[0]; return n ? Math.round(n.domContentLoadedEventEnd) : -1; } catch (e) { return -1; } })(),
/* ── the weather: what the screen says when a read fails, an answer
is empty, or nobody is signed in. Every one of these is a line
this round put there; before it they were an empty box, a spinner
@@ -904,7 +919,28 @@ const MOCK_EXPECT = {
"landscape-artist": (i) => i.heroH > 0 && i.heroH <= 200 && i.actsIn,
"landscape-insets": (i) => i.miniLeft >= 34 && i.screenPadL >= 26,
"portrait-again": (i) => i.vp === "411x914" && i.sheet.includes("up") && i.npCols === "stacked" && i.npOut === 0 && i.chromePct <= 24,
home: (i) => /^(Good |Still)/.test(i.title) && /^Home\*\|Search\|Library\|Community(, \d+ unread)?\|You(, \d+ transfers?)?$/.test(i.navTabs) && !i.a11yAnon && !i.a11yGhost && !i.a11yBad && !i.a11yState,
home: (i) => /^(Good |Still)/.test(i.title) && /^Home\*\|Search\|Library\|Community(, \d+ unread)?\|You(, \d+ transfers?)?$/.test(i.navTabs) && !i.a11yAnon && !i.a11yGhost && !i.a11yBad && !i.a11yState
/* and the boot budget, read off __ss.boot. Each clause earns its
place: the census must have counted something (a census that
breaks and reports -1 or -2 would sail through a one-sided
test); it must not have counted the world; the boot chain must
still be going out in one round rather than waiting on itself,
which is the one clause that is not a clock at all — it was 10
before round 32 and is 23 after, so a chain put back in a queue
lands under the floor; the shell must have sealed on a frame
rather than on the 1200 ms fallback; the library must still land
inside the launch rather than behind something slow; and round
12's eight-second art prewarm must still be nowhere near boot.
The call ceiling is deliberately loose — the boot chain issues
about 25 commands and the first screen asks for its covers on
top, and the point there is to catch a round that pulls a
per-artist walk or an index to boot, not to police a handful of
calls. */
&& i.bootCalls >= 14 && i.bootCalls <= 60
&& i.bootSync >= 20
&& i.bootMs >= 0 && i.bootMs < 1200
&& i.bootLibMs >= 0 && i.bootLibMs < 600
&& !/library_art_prewarm/.test(i.bootCmds),
"hold-sheet": (i) => i.half.includes("up") && /Re-fetch the cover/.test(i.halfText),
"cover-refetched": (i) => !i.half.includes("up") && /Cover re-fetched/.test(i.notice),
"search-empty": (i) => i.title.startsWith("Search") && !i.half.includes("up"),
@@ -1222,6 +1258,16 @@ const PHONE_STEPS = [
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "end", wait: 800 },
];
const PHONE_EXPECT = {
/* the phone's own launch, held wider on purpose: a real one signs in,
restores a session and may walk the follows, none of which the mock
does, and its library reads are real SQLite rather than an object in
memory. What is asserted here is the shape — a census that counted
something, sealed, still went out in one round, and did not drag the
art prewarm back to boot. The milliseconds are printed rather than
policed: every step's JSON carries bootMs and bootLibMs, so the
device's real figures land in the report the first time this walk
runs. */
home: (i) => i.bootCalls >= 14 && i.bootCalls <= 90 && i.bootSync >= 20 && i.bootMs >= 0 && !/library_art_prewarm/.test(i.bootCmds),
album: (i) => i.screen.includes("tr-push") && i.amb.includes("on:img") && !i.a11yAnon && !i.a11yGhost,
"album-playing": (i) => i.mini && !i.toast,
"lyrics-sheet": (i) => i.half.includes("up") && /Search again/.test(i.halfText),