Bug
dpx-deck-splash.service never automatically comes back if the currently-active mode service dies and its own Restart=on-failure retry limit is exhausted. The deck is left frozen on whatever was last drawn, with no live process listening for key presses — GO (or any key) appears completely unresponsive, but it's not a logic bug in execute_staged()/GO's handler; there's simply no process running to receive the press at all.
Root cause
dpx-deck-splash.service only has a Conflicts= relationship with the three mode services (bitfocus-buttons-usb-relay.service satellite.service companion.service) — mutual exclusion, starting one stops the others. Each mode service independently has its own Restart=on-failure. But nothing ties the two together in the other direction: if a mode service's own restart attempts exhaust (systemd's StartLimitBurst) and it ends up genuinely failed/inactive, nothing notices and starts dpx-deck-splash.service back up. Conflicts= alone doesn't do this — it only prevents simultaneous activity, it's not a trigger.
Confirmed live 2026-08-31 — reported as two symptoms that are actually the same root cause:
- "What's not clear is when the splash comes back" — currently, only if a mode is switched via the web UI/SSH; the deck itself has no path back to a live screen once its own process isn't running.
- "Sometimes when the device is already in a mode but not started, hitting GO does not start the thing" — GO's own logic (gotcha #15's
mode_dead check in execute_staged()) is correct and unaffected; the actual problem is there's no live dpx-deck-splash process to receive the keypress in the first place.
Proposed direction (needs a design decision, not blindly implemented)
Some kind of watchdog that starts dpx-deck-splash.service if none of the three mode services end up active, rather than relying solely on Conflicts=. Options to consider:
- A periodic timer unit that checks "is any mode service active?" and starts
dpx-deck-splash.service if not
OnFailure= on the mode services pointed at some intermediary check (risk: could fire on every transient restart attempt, not just final exhaustion — would fight the mode service's own recovery if not scoped carefully)
- Tie into the existing
switch_mode()/mode_service_active() pattern somehow, though that's currently only exercised from the web UI's own request path, not a standalone watchdog
Environment
- Confirmed on real Pi 4 hardware, build
main @ 8080ce3
- Likely applies to all boards/builds equally — this is a systemd unit relationship issue, not board/OS-specific
Bug
dpx-deck-splash.servicenever automatically comes back if the currently-active mode service dies and its ownRestart=on-failureretry limit is exhausted. The deck is left frozen on whatever was last drawn, with no live process listening for key presses — GO (or any key) appears completely unresponsive, but it's not a logic bug inexecute_staged()/GO's handler; there's simply no process running to receive the press at all.Root cause
dpx-deck-splash.serviceonly has aConflicts=relationship with the three mode services (bitfocus-buttons-usb-relay.service satellite.service companion.service) — mutual exclusion, starting one stops the others. Each mode service independently has its ownRestart=on-failure. But nothing ties the two together in the other direction: if a mode service's own restart attempts exhaust (systemd'sStartLimitBurst) and it ends up genuinelyfailed/inactive, nothing notices and startsdpx-deck-splash.serviceback up.Conflicts=alone doesn't do this — it only prevents simultaneous activity, it's not a trigger.Confirmed live 2026-08-31 — reported as two symptoms that are actually the same root cause:
mode_deadcheck inexecute_staged()) is correct and unaffected; the actual problem is there's no livedpx-deck-splashprocess to receive the keypress in the first place.Proposed direction (needs a design decision, not blindly implemented)
Some kind of watchdog that starts
dpx-deck-splash.serviceif none of the three mode services end up active, rather than relying solely onConflicts=. Options to consider:dpx-deck-splash.serviceif notOnFailure=on the mode services pointed at some intermediary check (risk: could fire on every transient restart attempt, not just final exhaustion — would fight the mode service's own recovery if not scoped carefully)switch_mode()/mode_service_active()pattern somehow, though that's currently only exercised from the web UI's own request path, not a standalone watchdogEnvironment
main@8080ce3