Files
James RichandClaude Sonnet 5 48d90a4296 fix(webApp): close 8 Koin DI gaps found by actually loading the app in a browser
Every gap here is a classpath-scanned Koin binding with no direct import
at the consuming call site (Koin resolves the dependency reflectively at
runtime), so none of them showed up in any compile-time check, detekt
pass, or the whole session's own "enumerate every import from a
wasmJs-excluded package" screening method -- only an actual browser load
of the compiled bundle surfaced them, one InstanceCreationException at a
time as Koin's object graph got further before failing on the next
missing binding.

Six honest no-op stubs (webApp/di/WebKoinModule.kt,
webApp/stub/NoopStubs.kt), each with its own KDoc explaining why:
ServiceDiscovery (mDNS/JmDNS, no browser mDNS API), SerialDevicePresence
(OS serial hot-plug, matches JVM/Desktop's own existing "no hot-plug
observation" empty-set precedent), SecurityKeyBackupStore and
LockdownPassphraseStore (EncryptedSharedPreferences/Keychain, deferred
pending a Web Crypto + IndexedDB design), FileService (arbitrary
CommonUri read/write, deferred pending File System Access API), and
LocaleChangeNotifier (OS locale-change signal, deferred pending a
languagechange listener -- a full page reload already re-reads
navigator.language fresh regardless). LocalNetworkAccess is not a stub
in the deferred sense: per its own doc contract ("granted-by-construction
everywhere the concept does not apply"), NoopLocalNetworkAccess
correctly returns true, since TCP is already rejected earlier by
WasmJsRadioTransportFactory (browsers cannot open raw sockets at all --
a permanent impossibility this specific Android-17 permission gate was
never about).

Two platform-registration wrappers, following the exact pattern
JvmScannerViewModel/JvmGetDiscoveredDevicesUseCase already establish for
their own platforms (the commonMain base classes are deliberately
un-annotated so each platform can register its own Koin binding without
the common one silently overriding a richer platform-specific one):
WasmJsGetDiscoveredDevicesUseCase (feature:connections, no UsbScanner --
WebUSB hot-plug scanning is a future pass) and WasmJsScannerViewModel
(feature:connections, bleScanner non-nullable since CoreBleWasmJsModule
always provides a real Web Bluetooth-backed BleScanner on this
platform, unlike Jvm/Android's optional one).

Verified via mcp__claude-in-chrome__: served
webApp/build/dist/wasmJs/productionExecutable/ locally with the
COOP/COEP headers webpack.config.d/coop-coep-headers.js already
declares, loaded it in a real Chrome tab, and iterated against the
console's actual InstanceCreationException chain until the app rendered
real Compose UI with zero console errors -- the Connect screen's
Bluetooth/Network/USB tabs, a working "Scan for Bluetooth devices"
action, and the nav rail correctly omitting Map (v0 scope, AC9).
Clicking Settings/Nodes creates their real ViewModels and triggers real
backend calls (FirmwareReleaseRepository fetching from the remote API);
whether they render full content without a connected device wasn't
further chased down, since that gating (if any) would be existing
androidApp/desktopApp behavior, not something this pass introduced.

Full repo baseline gate re-run clean after these additions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 05:59:11 -05:00
..