feat(gateway): per-platform gateway_restart_notification_channel override - #31046
Conversation
PR Review — #31046SummaryAdds an optional Author: julio-cloudvisor | Branch: CI StatusNo CI checks reported on this branch. Issues FoundWARNING (3)
INFO (2)
Positive Points
Recommendation
The gateway code itself is well-written with good comments, clean backwards compat, and a sensible fallback. My hesitation is primarily about the mixed scope and missing tests, not the implementation quality. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused gateway-routing proposal. The underlying problem remains on current main: active-session shutdown notices are still sent per target at gateway/run.py:5806.
Problems
gateway/run.py:3205treats every non-throwingadapter.send()as delivered. The adapter contract returnsSendResult; current lifecycle paths checksuccess=Falsebefore suppressing fallback (gateway/platforms/base.py:1878-1883,gateway/run.py:5806-5816).- The override only bypasses active-session shutdown notices. Home-channel shutdown delivery remains at
gateway/run.py:5862-5890, and startup/restart delivery remains atgateway/run.py:7300-7311and14828-14964, so the claimed single lifecycle notification is not guaranteed. - The new field is not added to the existing shared-key bridge at
gateway/config.py:1239-1242, so the supported top-level per-platform config shape will not propagate it. - Splitting all targets on
:breaks Matrix-style room IDs; current coverage explicitly exercises!room123:example.orgintests/gateway/test_restart_drain.py:515-538. - The unrelated Kanban PRAGMA change is already implemented on main at
hermes_cli/kanban_db.py:1729-1733and1761-1768.
Suggested changes
- Check
SendResult.success, cover all intended lifecycle paths, bridge and test both config shapes, use platform-safe target encoding, and omit the already-landed Kanban change.
Automated hermes-sweeper review.
| if ":" in override_target: | ||
| override_chat_id, override_thread_id = override_target.split(":", 1) | ||
| else: | ||
| override_chat_id, override_thread_id = override_target, None |
There was a problem hiding this comment.
adapter.send() may return SendResult(success=False) rather than raise. Please inspect that result before adding this platform to override_sent_platforms; otherwise a rejected override delivery suppresses the existing active-session fallback.
| if not override_target: | ||
| continue | ||
| adapter = self.adapters.get(platform) | ||
| if not adapter: |
There was a problem hiding this comment.
This generic colon split is not safe for every platform: Matrix room IDs themselves contain : (see the current Matrix shutdown-notification regression in tests/gateway/test_restart_drain.py). Use a platform-aware or unambiguous target representation.
| @@ -337,6 +356,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "PlatformConfig": | |||
| home_channel=home_channel, | |||
| reply_to_mode=data.get("reply_to_mode", "first"), | |||
| gateway_restart_notification=_coerce_bool(_grn, True), | |||
| gateway_restart_notification_channel=_grn_chan if _grn_chan else None, | |||
There was a problem hiding this comment.
Please also add this key to the shared-key bridge in load_gateway_config() beside gateway_restart_notification. This from_dict() fallback alone does not propagate the existing top-level per-platform config shape.
ca63746 to
10ee212
Compare
…ride
Add an optional per-platform `gateway_restart_notification_channel`. When
set, ALL lifecycle notifications for that platform are routed to that single
chat instead of the active-session sources and the home channel:
- shutdown/restart drain — the active-session pings AND the home-channel
broadcast (gateway/run.py _notify_active_sessions_of_shutdown)
- the post-restart "gateway restarted" ping (_send_restart_notification)
- the startup "gateway online" ping (_send_home_channel_startup_notifications)
Delivery is confirmed via SendResult.success — a platform is only treated as
handled by the override when the send actually lands; a failed override send
falls back to the normal per-session / home-channel delivery instead of
silently claiming the notification was delivered. The suppression flag
`gateway_restart_notification=False` continues to fully mute lifecycle pings
and takes precedence over the override.
The configured value is used verbatim as the chat id and is NOT split on ':'
— platform ids such as Matrix room ids ("!room:example.org") contain colons
and must survive intact (mirrors how home_channel.chat_id is treated).
The field is wired through both config shapes: the top-level per-platform
shared key (bridged into PlatformConfig via the shared-key loop in
load_gateway_config) and the nested `platforms:` block.
Tests: gateway lifecycle behavior (all paths, success=False fallback, Matrix
room id) in tests/gateway/test_restart_notification_channel_override.py;
config parsing/bridging for both shapes in tests/gateway/test_config.py.
|
Thanks for the review @jsboige, and apologies for the long silence on this one. I have addressed both points:
Test status after the rebase: Would appreciate another look when you get a chance. |
10ee212 to
46d0ed3
Compare
GottZ
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
Summary
Five PRs address or reference restart-notification targeting. #20801/#20892 provide the merged per-platform kill switch, #31046 routes every lifecycle notification to one operator channel, #52584 selectively suppresses bare channels while preserving DMs and threads, and #56015 gates existing targets through an allowlist rather than rerouting them.
Related pull requests
- #20801 [closed]
related— (+170/-4) — superseded by merged #20892: Adds the per-platform gateway_restart_notification boolean and gates shutdown, restart-originator, and startup/home notifications; it remains relevant as the original implementation whose commits were cherry-picked into #20892. - #20892 [merged]
related— (+171/-4) — merged reference implementation: Lands #20801's coarse per-platform notification opt-out with config round-trip/coercion and coverage across all lifecycle paths, but does not provide a dedicated destination for enabled notifications. - #31046
related— (+413/-0) — merge: Extends the merged boolean with a single per-platform override destination and routes shutdown session/home, post-restart, and startup notifications there. Consistent with the keep_open review on #31046, the revised diff addresses its blockers by checking SendResult.success with fallback, bridging both config shapes, covering every lifecycle path, preserving colon-bearing Matrix room IDs verbatim, and removing the unrelated SQLite change. - #52584 [closed]
related— (+366/-15) — distinct closed alternative: Adds a boolean that suppresses lifecycle notices in bare channels/groups while retaining DMs and active threads, requiring persisted chat_type data; it reduces noise but does not consolidate notifications into the single ops destination requested by #31046. - #56015 [closed]
related— (+196/-10) — closed alternative, superseded by tracking #52584/#31046: Adds an allowlist that suppresses delivery to existing targets outside selected IDs, but it does not reroute lifecycle events to an ops channel when that channel was not already a delivery target.
Duplicates
#20801 and #20892 implement substantially the same coarse per-platform opt-out; #20892 is the merged salvage of #20801. #31046, #52584, and #56015 overlap in reducing lifecycle-notification noise but have materially different routing semantics.
Suggested consolidation
Merge #31046 — it builds on the already-merged #20892 kill switch and directly solves the remaining single-ops-channel routing need across all lifecycle paths, with the contributor's keep_open review issues addressed in the current diff. #20801 is already superseded by #20892; #52584 and #56015 are already closed and should remain reference alternatives rather than duplicates of #31046.
Cross-PR triage: Reviewed 5 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 85 kB of PR diffs, 16 kB of issue/PR text, 22 kB of discussion (6 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.
What this adds
An optional per-platform
gateway_restart_notification_channel. When set, all lifecycle notifications for that platform are routed to that single chat instead of the active-session sources and the home channel — so operators can watch restarts in an ops channel (e.g.#ops-alerts) without pinging end-user channels during active conversations.Rework in response to review
Thanks for the detailed review — every point below is addressed. The branch was rebased onto current
mainfirst; several of the cited line numbers had moved and the kanban change had already landed, so I resolved against the current source and note the deltas inline.Review asks → changes
Check
SendResult.success, not just non-throw. Override delivery now goes through a single helper_send_lifecycle_override_notification()that returnsTrueonly when the adapter does not reportsuccess=False. On failure it returnsFalseand the caller falls back to the normal per-session / home-channel delivery — no more silently-claimed delivery. Mirrors the existing lifecyclesuccess=Falsechecks in_notify_active_sessions_of_shutdown/_send_restart_notification.Test:
test_override_send_failure_falls_back_to_active_session.Cover ALL lifecycle paths. The override now routes every lifecycle path (and only it, when a send succeeds):
_notify_active_sessions_of_shutdown(per-platform skip set)_send_restart_notification_send_home_channel_startup_notificationsTests:
test_shutdown_active_session_routed_to_override_channel,test_shutdown_home_channel_routed_to_override_channel,test_restart_notification_routed_to_override_channel,test_startup_notification_routed_to_override_channel, plustest_override_suppressed_when_restart_notification_flag_false(the existinggateway_restart_notification=Falsesuppression still wins).Wire the new field into the shared-key config bridge. Added the
gateway_restart_notification_channelbridge line next togateway_restart_notificationin the shared-key loop inload_gateway_config(), and thefrom_dictfallback reads it fromextratoo (mirrors_grn/typing_indicator).Tests (both shapes):
test_bridges_gateway_restart_notification_channel_top_level_key(top-level shared key) andtest_bridges_gateway_restart_notification_channel_nested_platforms(nestedplatforms:block), plus unit-levelfrom_dict/to_dictroundtrip and..._resolved_from_extra.Platform-safe target encoding. Dropped the
:-split entirely. The configured value is used verbatim as the chat id, matching howhome_channel.chat_idis treated, so a Matrix room id like!room123:example.orgsurvives intact. Thread/topic targeting is not encoded into this single string precisely because platform ids can contain:; point the override at a channel/room directly.Tests:
test_override_channel_matrix_room_id_not_split_on_colon(runtime path) andtest_gateway_restart_notification_channel_matrix_room_id_preserved(config roundtrip).Drop the unrelated kanban PRAGMA change. Removed from the diff entirely (it had already landed on
mainathermes_cli/kanban_db.py). The PR is now single-purpose:gateway/config.py,gateway/run.py, and their tests only.Notes on cited line numbers
mainmoved since the review:typing_indicator/channel_overrideswere added toPlatformConfigand the shutdown path now builds metadata via_thread_metadata_for_target(...)and already checksSendResult.success. I integrated with those rather than the older shapes, so the concrete line numbers differ from the review — the function names above are the stable anchors.Tests
pytest tests/gateway/for the touched areas: new override file 7 passed, new config cases 7 passed, fulltest_config.py+test_restart_drain.py+test_restart_notification.py+test_gateway_shutdown.py+test_typing_indicator_toggle.py+ the new file = 186 passed, and the broader restart/startup/shutdown/lifecycle/drain/notification selection = 584 passed / 4 skipped, no regressions.