hermes-agent/tests/plugins/platforms/photon
ygd58 74939d2bfe fix(photon): stop sidecar-crash fatal handler from cancelling its own supervisor task
Fixes #73159.

When the Photon sidecar exits unexpectedly, _supervise_sidecar()
(running as self._sidecar_supervisor_task) correctly detects
SIDECAR_CRASHED and calls self._notify_fatal_error(). The Gateway's
fatal-error handler answers that by calling adapter.disconnect(),
which calls _stop_sidecar() -- from INSIDE the very task that's
currently executing this whole chain.

_stop_sidecar()'s cleanup unconditionally cancelled
self._sidecar_supervisor_task. Cancelling the currently-running task
raises CancelledError at its own next await point (inside
_notify_fatal_error() or _stop_sidecar() itself). Since
asyncio.CancelledError inherits from BaseException (not Exception),
the Gateway's `except Exception` guards around the fatal-error handler
don't catch it -- the handler aborts before ever reaching the
"queue platform for background reconnection" step. Photon then stays
permanently in `retrying` state until the whole process is manually
restarted, even though detection worked correctly and the underlying
transient upstream outage had long since recovered.

Fix: in _stop_sidecar()'s cleanup, check whether
self._sidecar_supervisor_task is asyncio.current_task() before
cancelling it. A task cannot legally cancel itself in any useful way
anyway (the cancellation only takes effect at its own next await,
which is exactly the corruption described above) -- when we're inside
the supervisor's own call stack, it's already in the process of
finishing on its own once _notify_fatal_error() returns, so skip the
cancel and just clear the reference.

This mirrors an existing precedent in the same file: disconnect()
already guards its OTHER task-cancellation (self._sidecar_health_task)
the same way (`if task is not asyncio.current_task()`), just not the
supervisor task in _stop_sidecar().

Per the issue's own note that existing tests mock out
_notify_fatal_error() entirely (so this integration chain was never
exercised), added two tests that drive the REAL chain: one runs
_supervise_sidecar() as an actual asyncio task with a real
_notify_fatal_error() that calls the real disconnect() -> _stop_sidecar(),
confirming the task completes without CancelledError and that the
post-disconnect reconnect-queue step actually executes; a second
confirms the OTHER call path (external cleanup, a different task)
still correctly cancels a running supervisor exactly as before.
Reverting only the adapter.py fix (keeping the new test) reproduces
the exact CancelledError from the bug report, confirming this is a
genuine regression test.

2 new tests pass; 113/113 in the full tests/plugins/platforms/photon/
directory (no regression to sidecar lifecycle, overflow recovery, or
health-monitoring behavior).
2026-07-28 21:45:52 -07:00
..
test_auth.py test(photon): allow auth.lock sentinel in temp-file leak assertions 2026-07-28 18:20:06 -07:00
test_check_requirements_risks.py fix(photon): unify sidecar-deps check, harden log unlink and truncation 2026-07-28 18:20:06 -07:00
test_fatal_notify_self_cancel.py fix(photon): dispatch fatal-error notification from a detached task 2026-07-28 21:45:52 -07:00
test_inbound.py fix(photon): ignore iMessage media placeholders 2026-07-28 18:17:52 -07:00
test_markdown.py feat(photon): upgrade to spectrum-ts 3.0.0 (pinned) with markdown + reactions 2026-06-12 01:07:38 -07:00
test_mention_gating.py feat(photon): gRPC-native iMessage channel (no webhook) 2026-06-08 21:03:58 -07:00
test_npm_error_log_regression.py fix(photon): unify sidecar-deps check, harden log unlink and truncation 2026-07-28 18:20:06 -07:00
test_outbound_media.py feat(photon): wire outbound media via spectrum-ts attachment() (#42397) 2026-06-08 15:29:16 -07:00
test_overflow_recovery.py fix(photon): stop sidecar-crash fatal handler from cancelling its own supervisor task 2026-07-28 21:45:52 -07:00
test_reactions.py fix(photon): correlate tapbacks to bot message context 2026-06-27 00:51:34 -07:00
test_runtime_record.py fix(photon): un-shadow the U+FFFC deferred-wait handler; fix stale sidecar-deps fixture 2026-07-28 21:28:25 -07:00
test_setup_access.py test(photon): stub token validation in setup tests to avoid network 2026-07-28 18:20:06 -07:00
test_sidecar_deps_stale.py fix(photon): auto-reinstall stale sidecar deps before start 2026-07-05 17:38:32 -07:00
test_sidecar_lifecycle.py fix(photon): unify sidecar-deps check, harden log unlink and truncation 2026-07-28 18:20:06 -07:00
test_spectrum_patch.py fix(photon): keep sidecar alive if spectrum patch fails 2026-07-28 21:45:52 -07:00
test_streaming.py fix(photon): mark adapter as not supporting message editing to suppress streaming cursor 2026-07-28 18:17:52 -07:00