Phase 4. Declares jvm(), implements all 16 expects, and bumps the
submodule to the fork branch carrying phases 1-3.
:composeApp:compileKotlinJvm is green.
**The actuals were the small half. Room was the blocker.** The first jvm
compile failed with 58 copies of "Only suspend functions are allowed in
DAOs declared in source sets targeting non-Android platforms". Room
permits blocking query methods on android and nowhere else, so every @Dao
function that was neither suspend nor Flow-returning had to change --
58 of them across 24 files. KSP reports these in alphabetical batches, so
the count shrinks in stages and looks bottomless; scanning the dao package
directly for abstract funs with no suspend and no Flow return finds them
all at once.
It stops there, which is the only reason this is a 58-line change rather
than a refactor. Every one of the 15 call sites outside the dao package
was already inside a suspend function -- the repositories were written
that way throughout -- so nothing needed rewriting. One private helper,
DatabaseNostrRepository.matchNegentropicNostrEvents, had to become
suspend, and its single caller was already suspend, so the cascade
terminated immediately. Zero call-site edits.
**The cost lands on android, not on the jvm.** A blocking DAO method runs
on its caller's thread; a suspend one is dispatched to the query coroutine
context, which getRoomDatabase sets to Dispatchers.IO. That is the better
behaviour -- it is what stops a query running on the main thread -- but it
is a real change to the shipping platform, made for a target that does not
run yet. Hence the unit tests below rather than a compile alone.
**BusinessManager was not an expect**, so nothing warned about it. It is
now ported to the fork's jvmMain (05ce7eb); Phoenix.jvm.kt and
NavigationViewModel.jvm.kt are otherwise the ios actuals with one changed
import, since those files use no ios API.
**schedulePlatformLogic schedules nothing, and logs that it does not.**
Android starts two WorkManager jobs here, one of which is ChannelsWatcher
-- it wakes periodically to notice a channel force-closed while the app
was shut. A desktop application has no process once its window closes, so
there is nothing to wake, and running the watcher in-process would be
strictly worse than not running it: it would only fire while the app was
already open and watching. The exposure is real and belongs in release
notes rather than a comment -- a desktop wallet left closed past a
force-close deadline does not notice.
Smaller calls. PlatformContext carries an application directory, since
there is no Context to read one from, and PlatformDatabaseBuilder puts
aux.db under it rather than in java.io.tmpdir, which is what the abandoned
Aux implementation did behind a TODO and which most systems clear on
reboot. themeColorScheme ignores dynamicColor, which means Material You
and has no desktop counterpart. AppVersion reads the jar manifest that
compose.desktop writes, falling back when running from a class directory.
Verified: :composeApp:compileKotlinJvm green,
:composeApp:compileDebugKotlinAndroid green, and
:composeApp:testDebugUnitTest 52 passing -- the one that matters, since
this commit changes shared code every android query path goes through.
Not verified: nothing has run. No jvm entry point exists yet, so the
database has never been opened on this platform and no business has been
started. That is phase 5, which also has to unlock JvmKeyStore before the
wallet starts -- a passphrase prompt, not just a window.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>