fix(display): a raising display() counts as a circuit-breaker failure (#707)

The first-frame dispatch (_dispatch_first_frame) now asks PluginExecutor.execute_display() to re-raise (raise_errors=True) and records a raise inside the executor as a breaker failure, with the original exception as last_error, instead of a success. The screen is still an empty pass and rotation is unchanged; a hung display() is still recorded once, as a hang. The run-loop golden trace plugin_error.json is regenerated (crashy now records health failures and is skipped by the breaker), and behaviour 7 is dropped from docs/RUN_LOOP_REDESIGN.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-01 14:51:38 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a21650e746
commit 9edeb6da14
7 changed files with 361 additions and 12 deletions
+13 -1
View File
@@ -173,7 +173,8 @@ class PluginExecutor:
force_clear: bool = False,
display_mode: Optional[str] = None,
timeout: Optional[float] = None,
accepts_display_mode: Optional[bool] = None
accepts_display_mode: Optional[bool] = None,
raise_errors: bool = False
) -> bool:
"""
Execute plugin display() method with error handling.
@@ -187,9 +188,18 @@ class PluginExecutor:
accepts_display_mode: Whether plugin.display() takes a
display_mode keyword. Pass it when the caller already knows;
None falls back to inspecting the callable.
raise_errors: Re-raise the PluginError wrapping an exception
display() raised, instead of returning False. False alone
cannot tell "no content" from "raised", and a caller that
feeds the circuit breaker needs that difference. The error
is still logged and recorded first. A timeout still returns
False either way.
Returns:
True if display succeeded, False otherwise
Raises:
PluginError: Only with ``raise_errors``, when display() raised.
"""
try:
start_time = time.monotonic()
@@ -245,6 +255,8 @@ class PluginExecutor:
return False
except PluginError:
# Already logged and recorded in execute_with_timeout
if raise_errors:
raise
return False
except Exception as e:
self.logger.error(