The hand-back never checked that the panel came back, and a crashed host left game mode asleep #375

Merged
enricobuehler merged 1 commits from worktree-gamescope-idled-handback into main 2026-08-22 23:38:26 +00:00
Owner

Field reports on 0.31.x, Bazzite and Nobara: after disconnecting, the box's own physical screen stays black.

I could not reproduce it. The previous comment has the full negative write-up — five scenarios across both distro families on the real VMs, all recovering cleanly, and the mechanism the first version of this PR proposed disproved on glass. The branch has been rewritten from scratch on that basis.

So this does not guess at the trigger. It closes the gap that lets any trigger end as a dark panel, and fixes the one black-screen path I could prove.

1. The restore never checked its own work

do_restore_tv_session issues a lifecycle verb and logs what systemd said about the job. "The job succeeded" and "the box shows a picture" are different questions, and nothing in this file has ever asked the second one — the restore walks away the moment the verb returns, so every way the box can end up dark looks identical to success in the log.

So measure it. A detached watcher polls detect_active_session(), whose None means no compositor of our uid is running at all — exactly the symptom. Still dark 25 s after the hand-back, it climbs a ladder, each rung measured on both images (Bazzite 44.20260818, Nobara f44, 2026-08-22):

  1. stop the autologin unit. Its login session's script is parked on systemctl --user --wait start <unit> on both images, so a stop releases that wait, the session exits, and Relogin=true logs back in — starting the unit inside a session with a seat. stop, not restart: a restart does not release the parked waiter (measured), which is exactly why it cannot rescue a box the ordinary restart already failed to bring back.
  2. Restart the display manager — what the pre-0.31.0 takeover did on every disconnect, and proven on the Bazzite VM to return the box to game mode.
  3. PUNKTFUNK_RECOVER_SESSION_CMD, then an ERROR naming the command a human has to run.

Detached, and that is load-bearing. The restore holds RESTORE_FLIGHT, which a reconnecting client must take before it can re-take the box; watching for up to a minute while holding it would put that wait in front of every reconnect. The watcher also stands down the instant takeover_live() says a new takeover armed — the box belongs to that stream now, and a remedy fired into it would be a fresh bug. It runs after clear_takeover() so that check means "a client reconnected", not "our own takeover has not been filed yet".

Skipped on the shutdown path: restore_takeover_now runs inside native.rs's 20 s SHUTDOWN_RESTORE_GRACE, and spending that grace watching would cost the hand-back rather than check it. What covers a shutdown that left the box dark is the next host start — which this PR also makes true.

2. A crashed host left the box's game mode asleep — provably

restore_takeover_on_startup sweeps a leftover idle drop-in and logs that the box's "own Game Mode session would have started and then done nothing". Removing the file does not touch the unit running under it: its ExecStart is still the sleep, so it sits active drawing nothing. Nothing below that sweep restarts it either — the takeover file may be absent, unparseable, or fail takeover_state_is_live, and all three exits leave the box on a dark panel with its game mode "running". Any host killed mid-takeover (SIGKILL, OOM, a yanked update) lands exactly there, and it survives until someone reboots.

hand_back_idled_units_after_crash restarts those units — gated on the box actually being dark, so a user already in game mode or on a desktop is never bounced, and only for active instances (under a just-removed idle drop-in, active means "running the sleep").

Deliberately not changed

The restart verb on the ordinary restore path. It works on both distros (measured), and 0.31.0 chose it deliberately for the idled unit. The stop idea from v1 of this PR survives only as escalation rung 1, where it runs after the proven path has already failed.

Checks

  • cargo clippy -p pf-vdisplay --all-targets -- -D warnings — clean.
  • cargo fmt — clean.
  • cargo test -p pf-vdisplay --lib — 248 passed, 0 failed.
  • listed_autologin_units factored out of stop_autologin_sessions so both callers share it; its column parsing — which decides whether a live gaming session can be told from a dead leftover — finally has a test against real --plain output from both images.

On-glass validation of both halves on the VMs follows, against a canary built from this branch.

Field reports on `0.31.x`, Bazzite and Nobara: after disconnecting, the box's own physical screen stays black. **I could not reproduce it.** The [previous comment](https://git.unom.io/unom/punktfunk/pulls/375#issuecomment-4320) has the full negative write-up — five scenarios across both distro families on the real VMs, all recovering cleanly, and the mechanism the first version of this PR proposed disproved on glass. The branch has been rewritten from scratch on that basis. So this does not guess at the trigger. It closes the gap that lets *any* trigger end as a dark panel, and fixes the one black-screen path I could prove. ## 1. The restore never checked its own work `do_restore_tv_session` issues a lifecycle verb and logs what systemd said about the **job**. "The job succeeded" and "the box shows a picture" are different questions, and nothing in this file has ever asked the second one — the restore walks away the moment the verb returns, so every way the box can end up dark looks identical to success in the log. So measure it. A detached watcher polls `detect_active_session()`, whose `None` means no compositor of our uid is running at all — exactly the symptom. Still dark 25 s after the hand-back, it climbs a ladder, each rung measured on both images (Bazzite `44.20260818`, Nobara f44, 2026-08-22): 1. **`stop` the autologin unit.** Its login session's script is parked on `systemctl --user --wait start <unit>` on both images, so a stop releases that wait, the session exits, and `Relogin=true` logs back in — starting the unit inside a session **with a seat**. `stop`, not `restart`: a restart does *not* release the parked waiter (measured), which is exactly why it cannot rescue a box the ordinary restart already failed to bring back. 2. **Restart the display manager** — what the pre-0.31.0 takeover did on every disconnect, and proven on the Bazzite VM to return the box to game mode. 3. **`PUNKTFUNK_RECOVER_SESSION_CMD`**, then an ERROR naming the command a human has to run. **Detached, and that is load-bearing.** The restore holds `RESTORE_FLIGHT`, which a reconnecting client must take before it can re-take the box; watching for up to a minute while holding it would put that wait in front of every reconnect. The watcher also stands down the instant `takeover_live()` says a new takeover armed — the box belongs to that stream now, and a remedy fired into it would be a fresh bug. It runs *after* `clear_takeover()` so that check means "a client reconnected", not "our own takeover has not been filed yet". Skipped on the shutdown path: `restore_takeover_now` runs inside `native.rs`'s 20 s `SHUTDOWN_RESTORE_GRACE`, and spending that grace watching would cost the hand-back rather than check it. What covers a shutdown that left the box dark is the next host start — which this PR also makes true. ## 2. A crashed host left the box's game mode asleep — provably `restore_takeover_on_startup` sweeps a leftover idle drop-in and logs that the box's *"own Game Mode session would have started and then done nothing"*. Removing the **file** does not touch the unit **running under it**: its `ExecStart` is still the sleep, so it sits `active` drawing nothing. Nothing below that sweep restarts it either — the takeover file may be absent, unparseable, or fail `takeover_state_is_live`, and all three exits leave the box on a dark panel with its game mode "running". Any host killed mid-takeover (SIGKILL, OOM, a yanked update) lands exactly there, and it survives until someone reboots. `hand_back_idled_units_after_crash` restarts those units — gated on the box actually being dark, so a user already in game mode or on a desktop is never bounced, and only for `active` instances (under a just-removed idle drop-in, active means "running the sleep"). ## Deliberately not changed The `restart` verb on the ordinary restore path. It works on both distros (measured), and 0.31.0 chose it deliberately for the idled unit. The `stop` idea from v1 of this PR survives only as escalation rung 1, where it runs *after* the proven path has already failed. ## Checks - `cargo clippy -p pf-vdisplay --all-targets -- -D warnings` — clean. - `cargo fmt` — clean. - `cargo test -p pf-vdisplay --lib` — 248 passed, 0 failed. - `listed_autologin_units` factored out of `stop_autologin_sessions` so both callers share it; its column parsing — which decides whether a live gaming session can be told from a dead leftover — finally has a test against real `--plain` output from both images. On-glass validation of both halves on the VMs follows, against a canary built from this branch.
Author
Owner

On-glass validation: could NOT reproduce the bug. Do not merge as-is.

I drove the real path on both VMs (home-bazzite-2 119, home-nobara-1 123 on home-node-3) with the headless punktfunk-probe against the pre-fix canary 0.32.0-0.ci15103.g2b91339c. The reported black screen did not reproduce in any scenario, and the premise of this PR is wrong on two counts.

What the boxes actually do

Both images satisfy every precondition I reasoned about:

Bazzite 44.20260818.0 Nobara (f44)
DM sddm.service plasmalogin.service
Relogin=true yes (/usr/lib/sddm/sddm.conf.d/holo.conf) yes (/etc/plasmalogin.conf)
session script parked on systemctl --user --wait start gamescope-session-plus@<x>.service same
unit @ogui-steam @steam

Scenarios run (all recovered cleanly)

  1. Bazzite, control on the old DM-stop build (0.31.0-0.ci14629.gd161c126) — took the DM branch, restored the display manager, box back. Confirms the pre-0.31.0 half of the write-up.
  2. Bazzite, pre-fix idled build, 25 s session — idle drop-in + sleep infinity + relogin to session 428 during; after disconnect the drop-in is gone, gamescope is back in the same seat0 session, dpms=On, enabled=enabled.
  3. Bazzite, immediate-disconnect race (keep-alive secs=0, so the restore fires inside the relogin window) — clean.
  4. Nobara, pre-fix idled build, 25 s sessionidled=true, relogin to session 11, then removed the takeover's idle drop-in + restored the TV's autologin gaming session, gamescope back, dpms=On.
  5. Nobara, 3 rapid connect/disconnect cycles — clean.

Where the reasoning was wrong

  • systemctl --user --wait start does not return on a restart. It only returns on a genuine deactivation. So the host's restart never ends the login session, gamescope starts inside the live seat0 session and gets DRM master normally. The seat-less race this PR is built on does not happen. Verified by hand on Bazzite before touching punktfunk at all.
  • The loop reaching DM boxes is deliberate, not a regression. d9662c01 chose that verb for exactly this case — "restart, not start: the idle takeover leaves the unit ACTIVE" — and it works on both distros.

So this PR would replace a working, intentional design with a different one on an unproven theory. Converting to WIP.

What is still worth keeping

await_relogin_or_start's backstop reasoning and the handback_verb test are cheap, but they defend against a failure nobody has demonstrated. Parking until there is a real reproduction.

To actually diagnose this

Needed from the two reporters:

  1. journalctl --user -u punktfunk-host --since <disconnect> — the host logs every restore decision by name (scheduled TV-session restore, removed the takeover's idle drop-in, restored the TV's autologin gaming session, or the desktop-active early return). Their log will name the branch taken.
  2. What "black" means: SDDM/plasmalogin greeter, no signal at all, or Game Mode simply not returning?
  3. systemctl --user status gamescope-session-plus@*.service, loginctl list-sessions, and cat /sys/class/drm/*/dpms while it is black.
  4. Whether a game was running, and whether the box had been switched to desktop and back during the session — the desktop-active early return at do_restore_tv_session is the one path that legitimately leaves the box un-restored, and it is my leading remaining suspect.

My rig differs from the field in one way worth noting: the video pipeline never delivered frames on either VM (no PipeWire frame within 2.5s … the compositor produced no frames), so these were takeover/teardown exercises rather than fully streaming sessions with a game loaded.

## On-glass validation: could NOT reproduce the bug. Do not merge as-is. I drove the real path on both VMs (`home-bazzite-2` 119, `home-nobara-1` 123 on home-node-3) with the headless `punktfunk-probe` against the pre-fix canary `0.32.0-0.ci15103.g2b91339c`. **The reported black screen did not reproduce in any scenario**, and the premise of this PR is wrong on two counts. ### What the boxes actually do Both images satisfy every precondition I reasoned about: | | Bazzite `44.20260818.0` | Nobara (f44) | |---|---|---| | DM | `sddm.service` | `plasmalogin.service` | | `Relogin=true` | yes (`/usr/lib/sddm/sddm.conf.d/holo.conf`) | yes (`/etc/plasmalogin.conf`) | | session script | parked on `systemctl --user --wait start gamescope-session-plus@<x>.service` | same | | unit | `@ogui-steam` | `@steam` | ### Scenarios run (all recovered cleanly) 1. **Bazzite, control on the old DM-stop build** (`0.31.0-0.ci14629.gd161c126`) — took the DM branch, `restored the display manager`, box back. Confirms the pre-0.31.0 half of the write-up. 2. **Bazzite, pre-fix idled build, 25 s session** — idle drop-in + `sleep infinity` + relogin to session 428 during; after disconnect the drop-in is gone, gamescope is back **in the same seat0 session**, `dpms=On`, `enabled=enabled`. 3. **Bazzite, immediate-disconnect race** (keep-alive `secs=0`, so the restore fires inside the relogin window) — clean. 4. **Nobara, pre-fix idled build, 25 s session** — `idled=true`, relogin to session 11, then `removed the takeover's idle drop-in` + `restored the TV's autologin gaming session`, gamescope back, `dpms=On`. 5. **Nobara, 3 rapid connect/disconnect cycles** — clean. ### Where the reasoning was wrong * **`systemctl --user --wait start` does not return on a `restart`.** It only returns on a genuine deactivation. So the host's `restart` never ends the login session, gamescope starts *inside* the live seat0 session and gets DRM master normally. The seat-less race this PR is built on does not happen. Verified by hand on Bazzite before touching punktfunk at all. * **The loop reaching DM boxes is deliberate, not a regression.** d9662c01 chose that verb for exactly this case — "`restart`, not `start`: the idle takeover leaves the unit ACTIVE" — and it works on both distros. So this PR would replace a working, intentional design with a different one on an unproven theory. Converting to WIP. ### What is still worth keeping `await_relogin_or_start`'s backstop reasoning and the `handback_verb` test are cheap, but they defend against a failure nobody has demonstrated. Parking until there is a real reproduction. ### To actually diagnose this Needed from the two reporters: 1. `journalctl --user -u punktfunk-host --since <disconnect>` — the host logs every restore decision by name (`scheduled TV-session restore`, `removed the takeover's idle drop-in`, `restored the TV's autologin gaming session`, or the desktop-active early return). Their log will name the branch taken. 2. What "black" means: SDDM/plasmalogin greeter, no signal at all, or Game Mode simply not returning? 3. `systemctl --user status gamescope-session-plus@*.service`, `loginctl list-sessions`, and `cat /sys/class/drm/*/dpms` **while it is black**. 4. Whether a game was running, and whether the box had been switched to desktop and back during the session — the desktop-active early return at `do_restore_tv_session` is the one path that legitimately leaves the box un-restored, and it is my leading remaining suspect. My rig differs from the field in one way worth noting: the video pipeline never delivered frames on either VM (`no PipeWire frame within 2.5s … the compositor produced no frames`), so these were takeover/teardown exercises rather than fully streaming sessions with a game loaded.
enricobuehler changed title from The idled takeover started the box's game mode with no seat, and the panel stayed black to WIP: The idled takeover's seat-less hand-back — NOT REPRODUCED, do not merge 2026-08-22 21:51:42 +00:00
enricobuehler added 1 commit 2026-08-22 23:02:43 +00:00
fix(gamescope): the hand-back never checked that the panel came back, and a crashed host left game mode asleep
ci / bun-nix (pull_request) Successful in 28s
ci / docs-drift (pull_request) Successful in 1m5s
ci / web (pull_request) Successful in 1m7s
ci / docs-site (pull_request) Successful in 1m50s
ci / rust-arm64 (pull_request) Successful in 2m7s
ci / rust (pull_request) Successful in 7m5s
android / android (pull_request) Successful in 7m35s
c63e8cee39
Field reports on 0.31.x, Bazzite and Nobara: after disconnecting, the box's own
physical screen stays black.

I could not reproduce it (PR #375 has the full negative write-up: five scenarios
across both distro families on the real VMs, all recovering cleanly, and the
mechanism I first proposed disproved on glass). So this does not guess at the
trigger. It closes the gap that lets ANY trigger end as a dark panel, and fixes
the one black-screen path I could prove.

## The restore never checked its own work

`do_restore_tv_session` issues a lifecycle verb and logs what systemd said about
the JOB. "The job succeeded" and "the box shows a picture" are different
questions, and nothing in this file has ever asked the second one — the restore
walks away the moment the verb returns, so every way the box can end up dark
looks identical to success in the log.

So measure it. After the hand-back a detached watcher polls
`detect_active_session()`, whose `None` means no compositor of our uid is running
at all — exactly the symptom. If the box is still dark 25 s later it climbs a
ladder of remedies, each measured on both images (Bazzite 44.20260818, Nobara
f44, 2026-08-22):

1. STOP the autologin unit. Its login session's script is parked on
   `systemctl --user --wait start <unit>` on both images, so a stop releases that
   wait, the session exits, and `Relogin=true` logs back in — starting the unit
   inside a session with a seat. `stop`, not `restart`: a restart does NOT
   release the parked waiter (measured), which is why it cannot rescue a box the
   ordinary restart already failed to bring back.
2. Restart the display manager — what the pre-0.31.0 takeover did on every
   disconnect, and proven on the Bazzite VM to return the box to game mode.
3. `PUNKTFUNK_RECOVER_SESSION_CMD`, then an ERROR naming the command a human has
   to run.

Detached, and that is load-bearing: the restore holds `RESTORE_FLIGHT`, which a
reconnecting client must take before it can re-take the box, so watching for up
to a minute while holding it would put that wait in front of every reconnect.
The watcher also stands down the instant `takeover_live()` says a new takeover
armed — the box belongs to that stream now, and a remedy fired into it would be
a fresh bug. It runs after `clear_takeover()` so that check means "a client
reconnected" and not "our own takeover has not been filed yet".

Skipped on the shutdown path: `restore_takeover_now` runs inside `native.rs`'s
20 s `SHUTDOWN_RESTORE_GRACE`, and spending that grace watching would cost the
hand-back rather than check it. What covers a shutdown that left the box dark is
the next host start — which this commit also makes true.

## A crashed host left the box's game mode asleep, provably

`restore_takeover_on_startup` sweeps a leftover idle drop-in off the box and logs
that the box's "own Game Mode session would have started and then done nothing".
Removing the FILE does not touch the unit RUNNING under it: its `ExecStart` is
still the sleep, so it sits `active` drawing nothing. Nothing below that sweep
restarts it either — the takeover file may be absent, unparseable, or fail
`takeover_state_is_live`, and all three exits leave the box on a dark panel with
its game mode "running". Any host killed mid-takeover (SIGKILL, OOM, a yanked
update) lands exactly there, and it survives until someone reboots.

`hand_back_idled_units_after_crash` restarts those units, gated on the box
actually being dark so a user already in game mode or on a desktop is never
bounced, and only for ACTIVE instances — under a just-removed idle drop-in,
active means "running the sleep".

## Not changed

The `restart` verb on the ordinary restore path. It works on both distros
(measured), and 0.31.0 chose it deliberately for the idled unit. The `stop` idea
survives only as escalation rung 1, where it runs after the proven path has
already failed.

`listed_autologin_units` is factored out of `stop_autologin_sessions` so both
callers share it, and its column parsing — which decides whether a live gaming
session can be told from a dead leftover — finally has a test against real
`--plain` output from both images.
enricobuehler force-pushed worktree-gamescope-idled-handback from a8a054f2b0 to c63e8cee39 2026-08-22 23:02:43 +00:00 Compare
enricobuehler changed title from WIP: The idled takeover's seat-less hand-back — NOT REPRODUCED, do not merge to The hand-back never checked that the panel came back, and a crashed host left game mode asleep 2026-08-22 23:03:15 +00:00
Author
Owner

On-glass validation: bug reproduced and fixed on BOTH distros

Canary 0.32.0-0.ci15147.gc63e8cee (built from this branch, run 19984) against pre-fix
0.32.0-0.ci15103.g2b91339c, on home-bazzite-2 (119) and home-nobara-1 (123).

The black screen, reproduced

Construct exactly what a host killed mid-takeover leaves behind — idle drop-in in place, unit killed and restarted under it, no takeover state file — then start the host so restore_takeover_on_startup sweeps it.

Pre-fix, both boxes:

----- AFTER HOST RESTART (the sweep ran) -----
dropin:                                     ← removed
ActiveState=active SubState=running
mainproc: /usr/bin/sleep infinity           ← unit still running the placeholder
box gamescope: 0                            ← nothing drawing
dpms: On                                    ← panel powered, and blank

with only removed a leftover idle drop-in … would have started and then done nothing in the log. Game Mode reports active, the panel is lit and black, and it stays that way until a reboot. That is the reported symptom, on the shipping build, on both distro families.

Post-fix, both boxes — same script, same box:

mainproc: /usr/bin/bash /usr/share/gamescope-session-plus/gamescope-session-plus <instance>
box gamescope: 1

Bazzite:

23:27:47 WARN  the box's Game Mode is running the dead host's idle placeholder and its panel is
               dark — restarting it  units=["gamescope-session-plus@ogui-steam.service"]
23:27:48 INFO  the box is driving its own panel again — hand-back complete

Nobara:

23:36:03 WARN  … units=["gamescope-session-plus@steam.service"]
23:36:04 INFO  the box is driving its own panel again — hand-back complete

No regression on the ordinary disconnect

A real streamed session (the Bazzite run delivered 747 frames with audio and latency percentiles — not a degraded takeover-only exercise), then disconnect:

23:31:48 freed Steam: … idled=true
23:32:14 removed the takeover's idle drop-in — the box's own Game Mode runs for real again
23:32:14 restored the TV's autologin gaming session (debounce elapsed, no client)
23:32:15 the box is driving its own panel again — hand-back complete

Same on Nobara (23:37:09). The verb is unchanged, the watcher confirms the outcome in ~0.5 s, and no escalation rung fired on either box — the ladder is dormant unless the box is actually dark.

crash gap pre-fix crash gap post-fix disconnect post-fix
Bazzite 44.20260818 black recovered works, verified
Nobara f44 black recovered works, verified

Scope note

This fixes a black screen that is definitely real and definitely reachable on the shipping build. Whether it is the one the two users hit is not proven — their trigger was "after disconnecting", and a clean disconnect recovers fine on both boxes here (see the previous comment). What makes this worth shipping regardless is the second half: the hand-back now measures its own outcome, so any other route to a dark panel gets caught and escalated instead of being logged as success. If a report survives this, the log will now name which rung failed.

## On-glass validation: bug reproduced and fixed on BOTH distros Canary `0.32.0-0.ci15147.gc63e8cee` (built from this branch, run 19984) against pre-fix `0.32.0-0.ci15103.g2b91339c`, on `home-bazzite-2` (119) and `home-nobara-1` (123). ### The black screen, reproduced Construct exactly what a host killed mid-takeover leaves behind — idle drop-in in place, unit killed and restarted under it, no takeover state file — then start the host so `restore_takeover_on_startup` sweeps it. **Pre-fix, both boxes:** ``` ----- AFTER HOST RESTART (the sweep ran) ----- dropin: ← removed ActiveState=active SubState=running mainproc: /usr/bin/sleep infinity ← unit still running the placeholder box gamescope: 0 ← nothing drawing dpms: On ← panel powered, and blank ``` with only `removed a leftover idle drop-in … would have started and then done nothing` in the log. Game Mode reports `active`, the panel is lit and black, and it stays that way until a reboot. That is the reported symptom, on the shipping build, on both distro families. **Post-fix, both boxes** — same script, same box: ``` mainproc: /usr/bin/bash /usr/share/gamescope-session-plus/gamescope-session-plus <instance> box gamescope: 1 ``` Bazzite: ``` 23:27:47 WARN the box's Game Mode is running the dead host's idle placeholder and its panel is dark — restarting it units=["gamescope-session-plus@ogui-steam.service"] 23:27:48 INFO the box is driving its own panel again — hand-back complete ``` Nobara: ``` 23:36:03 WARN … units=["gamescope-session-plus@steam.service"] 23:36:04 INFO the box is driving its own panel again — hand-back complete ``` ### No regression on the ordinary disconnect A real streamed session (the Bazzite run delivered 747 frames with audio and latency percentiles — not a degraded takeover-only exercise), then disconnect: ``` 23:31:48 freed Steam: … idled=true 23:32:14 removed the takeover's idle drop-in — the box's own Game Mode runs for real again 23:32:14 restored the TV's autologin gaming session (debounce elapsed, no client) 23:32:15 the box is driving its own panel again — hand-back complete ``` Same on Nobara (`23:37:09`). The verb is unchanged, the watcher confirms the outcome in ~0.5 s, and **no escalation rung fired** on either box — the ladder is dormant unless the box is actually dark. | | crash gap pre-fix | crash gap post-fix | disconnect post-fix | |---|---|---|---| | Bazzite `44.20260818` | **black** | recovered | works, verified | | Nobara f44 | **black** | recovered | works, verified | ### Scope note This fixes a black screen that is definitely real and definitely reachable on the shipping build. Whether it is *the* one the two users hit is not proven — their trigger was "after disconnecting", and a clean disconnect recovers fine on both boxes here (see the [previous comment](https://git.unom.io/unom/punktfunk/pulls/375#issuecomment-4320)). What makes this worth shipping regardless is the second half: the hand-back now measures its own outcome, so any *other* route to a dark panel gets caught and escalated instead of being logged as success. If a report survives this, the log will now name which rung failed.
enricobuehler merged commit db9cd40079 into main 2026-08-22 23:38:26 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#375