镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(display): a WiFi notice preempts the current screen and Vegas yields to it - #712

Merged
ChuckBuilds merged 2 commits into
mainfrom
claude/fix-wifi-notice-display
Oct 1, 2026
Merged

ChuckBuilds merged 2 commits into
mainfrom
claude/fix-wifi-notice-display

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Fixes two display-loop bugs that #704's golden traces recorded (items 1 and 2 of "Behaviour the traces pin down that may be wrong" in docs/RUN_LOOP_REDESIGN.md).

Root causes

  1. A notice posted during a screen was never shown. run() checked for a WiFi notice only at the top of a pass (_show_wifi_notice), which means only between screens. The frame loops, the make-up dwell and _sleep_with_plugin_updates never looked at it. A 5 s notice posted during a 20 s screen expired before that screen ended.
  2. Vegas yielded for a notice, then showed a rotation screen instead. _check_vegas_interrupt returned True for a pending notice, so run_iteration() returned False. The interrupted-Vegas branch then fell through to step 9 (one rotation screen) in the same pass. The notice expired during that screen.

Fix (src/display_controller.py)

  • New _wifi_notice_pending(): True when _check_wifi_status_message() has an unexpired notice and no on-demand session is active. On-demand still outranks WiFi, and the priority order is unchanged: follower, on-demand, WiFi, live, Vegas, rotation. The file stat is still throttled to 1 Hz, so polling it at frame rate is just a clock compare. It also rejects a cached result that has expired inside the throttle window.
  • It is polled where run() already checks for preemption:
    • 125 Hz and 1 Hz frame loops: break, as on a mode change.
    • Post-loop check: start the next pass, but only if the screen was cut short (not loop_completed). A screen that ran its full time still advances, and the notice shows before the next mode.
    • Make-up dwell: _sleep_with_plugin_updates now returns early when a notice becomes pending. This is edge-triggered, so the notice's own 0.5 s dwell does not cut itself short. It is skipped while the panel is scheduled off. After a dwell cut short, the pass ends without rotating.
    • After a Vegas iteration yields: if a notice is pending, end the pass so the next pass shows it (step 6) instead of falling through to a rotation screen.
  • A screen the notice interrupted resumes in full afterwards instead of being rotated past. This matches how an on-demand stop resumes the rotation.
  • The notice's lifetime is unchanged: timestamp + duration from the status file. Because it now preempts within about a second, it is on the panel for (almost) all of that time.

No restructuring toward the Arbiter. The diff only touches the WiFi paths.

Golden trace diffs

Only wifi_notice and vegas were regenerated. The other 13 goldens are byte-identical. The harness (reduce_trace) now labels a screen followed by <wifi> with exit reason wifi. No row in the other goldens is affected.

wifi_notice.json

     [0.0, "clock", 20.0, "duration", 20, false],
-    [20.0, "weather", 20.0, "duration", 20, true],
-    [40.0, "clock", 20.0, "on-demand-start", 20, true],
-    [60.0, "clock", 20.0, "on-demand-expired", 20, true],
+    [20.0, "weather", 5.0, "wifi", 6, true],
+    [25.0, "<wifi>", 5.0, "duration", 10, null],
+    [30.0, "weather", 20.0, "duration", 20, true],
+    [50.0, "clock", 20.0, "on-demand-start", 20, true],
+    [70.0, "clock", 10.0, "on-demand-expired", 10, true],
     [80.0, "<wifi>", 15.0, "duration", 30, null],
-    [95.0, "weather", 20.0, "duration", 20, true],
-    [115.0, "clock", 20.0, "duration", 20, true],
-    [135.0, "weather", 15.0, "horizon", 15, true]
+    [95.0, "clock", 20.0, "duration", 20, true],
+    [115.0, "weather", 20.0, "duration", 20, true],
+    [135.0, "clock", 15.0, "horizon", 15, true]
  • [20, weather, 5.0, wifi]: bug 1 fixed. The notice posted at t=25.0 cuts weather short at the 1 Hz loop's next check.
  • [25, <wifi>, 5.0]: the notice shows for its full 5 s (posted at 25, expires at 30). Before, it never appeared.
  • [30, weather, 20.0]: the interrupted mode comes back in full.
  • [50, clock, 20.0, on-demand-start]: the rotation now runs 10 s later. The on-demand request at t=60 is for clock, the mode already on screen, so that screen is not cut. This is unchanged behaviour for a same-mode request. The row's label comes from the request event at 60.
  • [70, clock, 10.0, on-demand-expired]: the on-demand screen, ended by the session's expiry at t=80 as before.
  • [80, <wifi>, 15.0]: unchanged. "AP mode on", posted at 65 during on-demand, waits for the session to end. This shows on-demand still outranking WiFi.
  • [95, clock], [115, weather], [135, clock]: the rotation resumes at the index saved when on-demand started. That is now clock, interrupted mid-screen at 60. Before, clock's screen had ended at exactly t=60 and advanced to weather before the request was read.

vegas.json

     [175.263, "<vegas>", 24.959, "vegas-interrupt", 3120, null],
-    [200.222, "clock", 20.0, "duration", 20, true],
-    [220.222, "<vegas>", 30.008, "duration", 3751, null],
-    [250.23, "<vegas>", 9.77, "horizon", 1222, null]
+    [200.222, "<wifi>", 3.0, "duration", 6, null],
+    [203.222, "<vegas>", 30.008, "duration", 3751, null],
+    [233.23, "<vegas>", 26.77, "horizon", 3347, null]
  • [200.222, <wifi>, 3.0]: bug 2 fixed. Vegas yields at 200.222 (its interrupt check cadence), and the 3 s notice is what shows next instead of a 20 s clock screen.
  • [203.222, <vegas>], [233.23, <vegas>]: Vegas resumes once the notice ends. The later rows shift to follow.

Tests

test/test_wifi_notice_preemption.py runs the real run() on the harness's fake clock:

  • a notice preempts a static (1 Hz) screen within 1 s, stays up until it expires, and the interrupted mode resumes before the rotation moves on
  • a notice preempts a high-FPS (enable_scrolling) screen within 1 s
  • a notice cuts the make-up dwell short (first_frame_only plugin), and that mode resumes
  • a screen that ran its full time is not repeated when a notice lands at its end
  • an active on-demand session still holds the panel; the notice shows after it expires
  • Vegas (FakeVegas): it yields for the notice, <wifi> shows next for the notice's full duration, then <vegas> resumes, with no rotation screen in between
  • _wifi_notice_pending() ignores an expired cached result and returns False during on-demand

Mutation check: I removed each of the 8 changes one at a time: the Vegas continue, each frame-loop check, the post-loop check, the not loop_completed guard, the dwell break, the post-dwell continue and the expiry guard. Every mutation fails at least one test.

Verification

  • pytest test/test_run_loop_golden.py test/test_wifi_notice_preemption.py: 23 passed.
  • Full suite, compared with origin/main run in a separate worktree: the FAILED/ERROR test IDs are identical (61 failed and 6 errors on both, all pre-existing Windows failures). The branch has 7 more passes, which are the new tests. No plugin-repos/ pollution.
  • scripts/check_types.py (mypy 1.8.0, run from a scratch venv): the same 5 errors as origin/main. They come from the venv lacking project dependencies, and none are in the files changed here.

Hardware check needed (not done; no rig touched)

On a Pi with a mixed static/scrolling rotation:

  1. Static screen: during a long static screen (e.g. clock at 30 s), trigger a WiFi notice (connect to a network or toggle AP mode from the web UI). The notice should replace the screen within about a second and stay up for its duration. The interrupted screen should then come back.
  2. Scrolling screen: repeat during a scrolling screen.
  3. Vegas: enable Vegas and trigger a notice mid-scroll. The ticker should stop, the notice should show, and Vegas should resume after it, with no rotation screen in between.
  4. On-demand: with an on-demand session running, trigger a notice. It must not interrupt the session.

The other run-loop fixes (claude/fix-live-takeover, claude/fix-schedule-boundaries) touch nearby lines in run() and docs/RUN_LOOP_REDESIGN.md. Expect small textual conflicts in the frame-loop conditions and the renumbered "exposed behaviour" list.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • WiFi notices now appear within about a second, interrupting the current screen rather than waiting for it to finish.
    • When a notice expires, the interrupted screen or mode resumes instead of being skipped. Vegas also resumes after the notice.
    • Active on-demand sessions retain control of the panel while WiFi notices are pending.

…lds to it

Two behaviours #704's golden traces recorded (docs/RUN_LOOP_REDESIGN.md,
"behaviour the traces pin down that may be wrong", items 1 and 2):

1. A WiFi notice was only checked at the top of a pass, i.e. between
   screens. A 5 s notice posted during a 20 s screen expired before the
   screen ended and was never shown.
2. Vegas's interrupt callback yielded for a notice, but run() then fell
   through to a rotation screen in the same pass, and the notice expired
   behind it.

Fix: a new _wifi_notice_pending() (on-demand still outranks it; the file
stat stays throttled to 1 Hz) is polled where run() already checks for
preemption: both frame loops, the post-loop check (only when the screen
was cut short, so a screen that ran its full time still advances), the
make-up dwell, and _sleep_with_plugin_updates (edge-triggered, so the
notice's own 0.5 s dwell does not cut itself short, and not while
scheduled off). After Vegas yields with a notice pending, the pass ends so
the next pass shows it. A screen the notice interrupts resumes in full
afterwards rather than being rotated past.

Goldens regenerated: wifi_notice and vegas only. The harness labels a
screen followed by a notice with exit reason "wifi".

wifi_notice:
- [20, weather, 5, wifi]: the notice posted at 25.0 cuts weather short.
- [25, <wifi>, 5]: shown for its full 5 s (posted 25, expires 30).
- [30, weather, 20]: the interrupted mode comes back in full.
- [50, clock, 20, on-demand-start]: the rotation is 10 s later; the
  on-demand request at 60 is for clock, the mode already showing, so the
  screen is not cut (unchanged behaviour for a same-mode request).
- [70, clock, 10, on-demand-expired]: the on-demand screen, ended by
  expiry at 80 as before.
- [95, clock ...], [115, weather ...], [135, clock ...]: rotation resumes
  at the index saved when on-demand started (clock, mid-screen at 60),
  where before clock's screen had ended at exactly 60 and advanced first.
  The second notice ("AP mode on", held back by on-demand) is unchanged.

vegas:
- [200.222, <wifi>, 3]: Vegas yields at 200.222 and the notice shows next
  instead of a 20 s clock screen.
- [203.222, <vegas> ...], [233.23, <vegas> ...]: Vegas resumes once the
  notice ends; the later rows shift accordingly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 738e59ce-a490-4f23-a246-cfba54fb0933

📥 Commits

Reviewing files that changed from the base of the PR and between 4be53d0 and 022a3ce.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/RUN_LOOP_REDESIGN.md
  • src/display_controller.py
  • test/_run_loop_harness.py
  • test/fixtures/run_loop_golden/vegas.json
  • test/fixtures/run_loop_golden/wifi_notice.json
  • test/test_run_loop_golden.py
  • test/test_wifi_notice_preemption.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

WiFi notices now interrupt active screens and dwell periods. After a notice, the controller resumes the interrupted mode when applicable. On-demand sessions retain control, and Vegas resumes after yielding to a notice.

Changes

WiFi notice preemption

Layer / File(s) Summary
Detect and yield to pending notices
src/display_controller.py, test/_run_loop_harness.py, test/test_wifi_notice_preemption.py, test/fixtures/run_loop_golden/wifi_notice.json
The controller checks whether an unexpired notice is pending, except during on-demand activity. Dwell and render loops yield when a notice becomes pending. Regression tests and the golden timeline cover notice preemption during static, high-FPS, and make-up-dwell screens.
Display notices and resume interrupted modes
src/display_controller.py, test/test_wifi_notice_preemption.py, test/test_run_loop_golden.py, test/fixtures/run_loop_golden/vegas.json, docs/RUN_LOOP_REDESIGN.md, CHANGELOG.md
The main loop displays a pending notice and resumes an incomplete screen or Vegas iteration afterward. Tests cover completed screens, on-demand precedence, and Vegas resumption. The run-loop plan and changelog describe the behavior.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 022a3

WiFi notices now interrupt the current screen and then resume it. On-demand sessions keep priority, and Vegas yields to the notice. No merge-blocking risk is evident from the supplied evidence. Hardware testing was not done.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 022a3

WiFi activity gains a broader ability to interrupt ordinary display content, but on-demand and follower controls remain in place. No new privilege gain or security-control bypass was established. Deployment access settings and some expiry and recovery behavior remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The traced incremental effect is interruption of ordinary content on the addressed device's display. A caller already permitted to trigger WiFi operations can now interrupt active screens rather than wait for their boundaries; repeated eligible notices can prolong that effect. The same caller already controls network-changing operations, and higher-priority on-demand and follower behavior limits the affected display scope.

Trust Boundaries and Controls

  • observed — The new interruption checks consume the existing mailbox rather than introduce another writer or privileged operation. On-demand suppression remains explicit, and follower display writes remain gated by the existing follower-render authority check.

Resilience and Maintainability Implications

  • observed — Dwell polling continues watchdog beats, plugin updates, and pending-change servicing. The mailbox reader catches read and validation failures instead of propagating them into the display loop, preserving the existing failure-containment behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 4 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: WiFi notices preempt the current screen, and Vegas yields to them.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 4 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds
ChuckBuilds merged commit 6c6394a into main Oct 1, 2026
13 checks passed
@ChuckBuilds
ChuckBuilds deleted the claude/fix-wifi-notice-display branch October 1, 2026 19:51
ChuckBuilds added a commit that referenced this pull request Oct 1, 2026
Conflicts with #712's WiFi-notice preemption, resolved to keep both:
- _sleep_with_plugin_updates docstring names both early returns.
- After an interrupted Vegas iteration the WiFi check comes first (it
  ends the pass so the next one shows the notice), then the live switch:
  the documented priority is follower, on-demand, WiFi, live, Vegas.
- CHANGELOG keeps both Fixes entries; RUN_LOOP_REDESIGN.md drops items
  1-4, leaving the two schedule items.

Adds test/test_run_loop_wifi_and_live.py: a notice and a game arriving
during the same screen (1 Hz, 125 Hz, Vegas) show the notice first, then
the game, and neither while scheduled off. Goldens regenerated: all
byte-identical to the merged files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Oct 1, 2026
… Vegas (#713)

A game that goes live takes over within about a second (_check_live_takeover in the frame loops and the dwell sleep, throttled to 1 s, never during on-demand, scheduled-off, live_in_ticker or an already-live screen); an interrupted Vegas iteration switches straight to the game; has_live_content() is asked once per plugin per scan. Goldens: live_priority, vegas.

Merged with #712: after an interrupted Vegas iteration the WiFi-notice check runs before the live switch (WiFi outranks live). Adds test/test_run_loop_wifi_and_live.py, pinning that a notice and a game arriving during the same screen (1 Hz, 125 Hz, Vegas) show the notice first, then the game, and neither while scheduled off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Oct 1, 2026
CHANGELOG keeps all Fixes entries. RUN_LOOP_REDESIGN.md: with #712 and
#713 in, removing items 5 and 6 empties the 'may be wrong' list, so the
section now says all six are fixed and by which PR. Goldens regenerated:
byte-identical to the merged files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChuckBuilds added a commit that referenced this pull request Oct 1, 2026
…in off hours blanks at once (#714)

Schedule and dim windows are half-open [start, end): on from the start time, off at exactly the end time, whatever second the check runs. An on-demand session that ends in scheduled-off hours (expiry or stop) clears the once-a-minute schedule gate, so the panel blanks within about a second. Golden: schedule; two test_display_pending_changes.py tests now say end_time 23:00.

Merged with #712 and #713: with all three in, docs/RUN_LOOP_REDESIGN.md's 'may be wrong' list is empty, so that section now records that all six items are fixed and by which PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ChuckBuilds

Copy link
Copy Markdown
Owner Author

Post-merge rig check on hdpi: SKIPPED (not run)

The run-loop checks were queued after the #702 soak, on origin/main 41db488, which contains #712, #713 and #714. They never ran.

At 20:07 EDT another session on hdpi made these changes:

  • 2x POST /api/v3/config/main (Vegas disabled, plugin order changed)
  • a display restart through POST /api/v3/system/action
  • POST /api/v3/plugins/config

My guard stopped the batch at that point. Their config was left as they set it, and HEAD was put back to 4ddc3a3.

Nothing about this PR was measured. For a re-run:

  • A WiFi notice can be triggered without touching the network or needing root. Write config/wifi_status.json with {message, timestamp, duration}; it is what _check_wifi_status_message reads.
  • hdpi runs Vegas all the time, so the static-screen case needs Vegas switched off for the test.
  • A favourite team was live during the window (NHL TB @ NYR, from 19:20 EDT), so the live-takeover check would have been possible.

🤖 Generated with Claude Code

ChuckBuilds added a commit that referenced this pull request Oct 2, 2026
…otice (#721)

The schedule-off blank and the WiFi notice are drawn by the display controller, not dispatched to a plugin, so #716's handover never reached them. Drawn while the last scroll's state was still set, the blank went out with the ticker's lagging rows on a scan-compensated panel and stayed up for its 60 s dwell, and the notice's redraws (which #712 now shows over a running scroller or Vegas) were timed as 0.5-1 s freezes and logged as a mid-scroll Render stall. The controller now calls set_scrolling_state(False) before drawing either; a scroller that resumes sets the state again on its next frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ChuckBuilds

Copy link
Copy Markdown
Owner Author

ledpi check (Pi 4, 192x48), main 746dcfc: PASS.

The notice was triggered by writing config/wifi_status.json. Times are from the write until the preview frame showed it. The preview writes at most once a second, so each time is an upper bound.

  • Vegas: the notice showed within 1.04 s. The Vegas iteration restarted 8.96 s after the write, about 1 s after the 8 s notice expired, with no rotation screen in between.
  • Static clock screen: the notice showed within 1.17 s and the clock screen broke early. The notice cleared at about 7.3 s for a 6 s notice, and the clock resumed.
  • On-demand (clock, pinned): the notice never showed and the session stayed active.

Minor observations:

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant