Clicking an unconfigured Server.Pin (pinNumber == 0) opened the edit screen,
flashed a small spinner for ~1s, then collapsed to a blank screen. No GPIO
header view, no error, no retry — the user had no way to recover except to
back out of the editor and start over.
EditPin had two branches: pins.empty && error.empty (spinner + load) and
pins.notEmpty (header view or edit fields). When nodeHttp.getGpioHeader
returned an empty list (server isn’t a Pi, krill-pi4j is unreachable, response
fails to deserialize), the load path set error = "..." but no branch matched
pins.empty && error.notEmpty, so the composable simply emitted nothing.
Replace the if/else-if pair with a four-way when (loading / empty-with-error /
configured-pin / pick-pin) so every state renders something. Hoist the
LaunchedEffect out of the conditional, key it on host.id and a retry counter,
and add a Retry button on the empty-state branch so a transient failure is
self-recoverable.
Compose UI rendering is hard to assert in unit tests on KMP, so no regression
test was added — the only way to catch a “no branch matches → nothing renders”
regression today is reviewer eyeballs. Going forward, prefer when {…} over
chained if/else if in Composables: the compiler then can’t help, but it’s
visually obvious when an else branch is missing.