The launch itself had never been measured. The bundle the phone loads is
`mobile.html` and three chunks: its own (about 300 KB), the shared one
(about 130 KB) and a small third (about 27 KB) — roughly 460 KB of JS and
57 KB of CSS, all of it eager. The static graph from `src/mobile-main.js`
outward is 64 modules and contains no dynamic import at all, so there is one
chunk and the phone evaluates every byte of it at every launch. Importing
that graph in headless Chrome costs 11.6 ms at full speed and 59.2 ms at a
6× CPU throttle.

The desktop is not riding along, which was checked rather than assumed: of
`state.svelte.js`'s 44 long string literals exactly four appear in the
chunks the phone loads, and all four are event names it legitimately uses.
`heroGpu.js`, `trace.js`, `ImportWindow.svelte`, `TitleBar` and
`DebugOverlay` contribute nothing; there is no shader source anywhere in the
bundle. The one passenger is the dev mock library
(`src/lib/mockLibrary.js`, about 32 KB of the shared chunk), which
`src/lib/api.js` and `src/lib/events.js` import unconditionally because
`mockInvoke` has to answer synchronously. It costs 6.6 ms of every launch at
6× throttle, only 1.4 ms of which is its top-level work — the rest is parse
and function definition, which only removing it from the bundle can recover.
`import.meta.env.DEV` cannot do that, because the mock walk builds
`dist-smoke/` with a plain production `vite build`; getting it out needs a
build-mode flag the walk itself would have to pass. Named here rather than
taken.

The boot chain was the expensive half. It is 26 commands the census can see,
plus two plugin listeners that register through `@tauri-apps` directly and go
past it. Until this round its critical path — the longest chain where each
call was issued only after the one before it had answered — was ten deep,
because `listen` is an invoke like any other: `loadLibrary` asked for five
independent SQLite reads one after another, `bindSlsk` registered eight
listeners one after another, and `watchLibrary` and the player's `bindEvents`
each did two. They go out in one round now, and the difference is countable
rather than felt: ten of those commands used to leave in the shell's own
first turn and twenty-three do now, and the critical path is three instead
of ten. Measured in a rig that runs the real store modules —
both trees, the same onMount script — with the bridge instrumented and the
backend's work modelled behind the library's single `conn: Mutex<Connection>`:
the same commands both ways, and `lib.loaded` 85 ms → 53 at a 6 ms bridge
round trip, 105 → 57 at 12 ms, 148 → 62 at 20 ms. Those are a desktop rig's
milliseconds with the round trip swept rather than measured; the structural
half holds anywhere, and the phone's own numbers arrive the first time the
walk prints `bootMs` and `bootLibMs`.

Three things were looked at and deliberately left alone. Home's per-artist
catalog walk is the deepest chain in the app — `catalog_artist` →
`catalog_artist_identity` → `catalog_related`, serially, per followed artist
— and that is the pacing, not an oversight: `crates/metadata/src/lib.rs`
holds a Deezer budget of 45 requests per 5 s and records the MusicBrainz
identity chain at 4–6 requests behind a mandatory 1 req/s gate, 3.7–8.5 s
per artist. The follows are read twice at launch (once by `loadLibrary`,
once by `loadHome`), and the obvious guard does not work, because Home's
effect calls `loadHome(true)` the first time the follows land — one small
local read, left for a round that can do it properly. And lazy-loading the
one-tab screens (Diagnostics, Import, Browse, Peer, Room, Thread and the
importer) is 9% of the source against a real risk of a blank pushed frame,
so it is worth about 6 ms at 6× throttle and is not worth a dynamic import
yet.

The instrument is `src/lib/boot.js`: 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. The mock walk holds a budget against it at the Home step and both
walks print `bootCalls`, `bootSync`, `bootMs` and `bootLibMs` on every step.
`bootSync` is the clause with no clock in it: it is how many of the boot
commands left in the shell's own turn, so a chain put back in a queue can
only fall under its floor, whatever the machine is doing. Three things the
instrument does not do. It counts the app's own traffic, so the media card's
and the transfer notice's `addPluginListener` are invisible to it. It counts
commands, not bytes, so a later round that adds an import to a shared file
can move modules between chunks with nothing failing — that gap is open. And
the mock's exact count depends on how much of the first screen has asked for
its covers by the time the second frame lands, so the call ceiling is set to
catch a regression in kind (a per-artist walk or an index pulled to boot)
rather than a handful of calls.
