The control socket is now the only way the web interface sends the display a
command. The display stops reading display_on_demand_request and
plugin_error_clear_request, and the web interface stops writing them.
- Display: no mailbox poll (MailboxWatch, the 1 s / 0.25 s cadence,
_consume_on_demand_request, the deprecation log) and no persisted
display_on_demand_processed_id guard; the error publisher reads no clear
request. CacheManager.file_signature and MailboxWatch are removed.
- A write to either retired key is dropped by CacheManager.save_cache and
logged once per writer, naming the plugin from the call stack (or the
request's plugin_id), with the API to move to.
- Web: on-demand start with no display listening starts the service (when
start_service) and sends the request again once the socket answers (45 s,
10 s for a running service without a socket yet); every other failure is
a 503 (400 for invalid_args). Stop answers 503 when no display listens,
unless stop_service. errors/clear answers 503 with a reason-specific
message instead of writing a request; clear_pending is always false.
src.ipc.client.should_fall_back is replaced by display_not_listening.
- Kept: display_current_state, display_on_demand_state,
plugin_runtime_snapshot and the heartbeat (read whenever the socket cannot
answer), and display_on_demand_config (the display's resume record).
Tests: mailbox-only tests removed (test_on_demand_mailbox.py, the mailbox
cadence, file_signature and MailboxWatch tests); tests that injected
requests through the mailbox now use the socket queue or a plugin's
in-process request. The run-loop harness sends on-demand requests over its
fake control socket, so four golden traces change: on-demand starts and
stops land at the request instant instead of the next 0.25 s mailbox look
(one frame fewer on the screen they end), and in vegas.json within one
frame instead of 263 ms, which shifts the later 1 s-throttled WiFi-notice
check by under a second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- client: ControlError.sent says whether the display had the request;
should_fall_back() allows a mailbox write only when it did not, or when
the display is too old to know the command (upgrade case)
- on-demand start/stop: a display that had the request and failed it is
answered 503 (400 for invalid_args), no mailbox copy
- errors.clear: new socket command, answered on the connection thread by
a handler the display registers; applied and republished before the
answer; plugin_error_clear_request only on fallback
- display: on-demand mailbox looked at once a second while the socket is
up (0.25 s without), read only when its file changed (one stat via
CacheManager.file_signature / MailboxWatch); socket commands no longer
touch the mailbox; a processed duplicate is consumed; writers logged once
- error publisher: mailbox read only when changed; snapshot carries
applied_clear_cutoff so an older mailbox request is not shown pending
- docs and CHANGELOG (mailboxes kept for one release)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On-demand: a mode requested by name is shown first (even a quiet live mode); a session that can't resume after a restart, or whose plugin system failed to start, ends with status restore-failed instead of staying dead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin.reload no longer freezes the panel during Vegas: the old instance is torn down and the new one loaded off the render thread (frame gap 3017 ms -> 9 ms in the ledpi reproduction). A failed or timed-out teardown stops the reload with a restart hint instead of loading over stale modules or tearing down an instance still in use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Control socket stage 2: the render thread wakes for queued commands (static screens ~1 ms, Vegas within one frame), brightness.set, and plugin.reload after a store update, with mailbox/restart fallbacks. Rig checks listed in the PR body are still to run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A WiFi notice preempts the current screen within about a second (frame loops, post-loop check, make-up dwell), and an interrupted Vegas iteration that yielded for a notice ends the pass so the notice shows next. Goldens: wifi_notice, vegas. First of three run-loop fixes (#712, #713, #714), pre-tested together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds golden trace tests for DisplayController.run() (test/test_run_loop_golden.py on a fake clock with fake plugins, 15 scenarios, fixtures in test/fixtures/run_loop_golden/) and moves twelve blocks of run() into named helpers (_dispatch_first_frame, _resolve_durations, _resolve_active_mode, _needs_high_fps, _advance_after_screen and others) with the traces identical before and after. docs/RUN_LOOP_REDESIGN.md describes the target structure. Hardware-checked on hdpi: Vegas late-frame rate unchanged in an ABBA A/B.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>