Skip to content

fix(gateway): bridge gateway_restart_notification from config.yaml platform sections (#24644)#24661

Closed
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/gateway-restart-notification-bridge-24644
Closed

fix(gateway): bridge gateway_restart_notification from config.yaml platform sections (#24644)#24661
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/gateway-restart-notification-bridge-24644

Conversation

@briandevans

@briandevans briandevans commented May 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Wires gateway_restart_notification: false (under a top-level platform section like telegram: in config.yaml) into the platform's PlatformConfig so the lifecycle-notification suppression at gateway/run.py:2767/:2810/:12801/:12860 actually fires. The feature was introduced in #20892 but the config bridge in load_gateway_config() was never updated, so setting the flag in config.yaml had no effect — confirmed by the issue's gateway log (notifications sent despite the setting).

PlatformConfig.gateway_restart_notification (gateway/config.py:281) is a dataclass field, serialized in to_dict(), parsed by from_dict() via _coerce_bool. Consumers in gateway/run.py read it directly as platform_cfg.gateway_restart_notification. But load_gateway_config() (gateway/config.py:758–825) — the bridge that copies top-level telegram:/slack:/etc. keys from config.yaml into the internal platforms dict — has an allow-listed bridge set with no entry for gateway_restart_notification.

The issue's suggested snippet would land the value inside extra (the bridge folds into extra.update(bridged)), but gateway_restart_notification is a top-level PlatformConfig field, not an extra key. Mirrors the enabled handling instead: track restart_notif_explicit, include it in the early-continue predicate so a section containing only this key isn't skipped, and assign plat_data["gateway_restart_notification"] directly alongside plat_data["enabled"]. _coerce_bool already covers false/"false"/0.

Related Issue

Fixes #24644

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/config.py — in load_gateway_config(), track restart_notif_explicit = "gateway_restart_notification" in platform_cfg, include it in the early-continue predicate, and assign plat_data["gateway_restart_notification"] directly (not via bridged/extra).
  • tests/gateway/test_config.py — 3 new cases in TestLoadGatewayConfig: bridges from config.yaml, defaults true when absent, bridges when only setting in the section. Belt-and-braces assertion that the value does NOT land in tg_cfg.extra.

How to Test

  1. uv run --with pytest --with pytest-xdist --with pytest-asyncio python3 -m pytest tests/gateway/test_config.py tests/gateway/test_restart_notification.py tests/gateway/test_restart_drain.py -v
  2. Expected: 87 passed.
  3. Bridge-adjacent: tests/gateway/test_telegram_group_gating.py tests/gateway/test_slack_mention.py — 72 passed.
  4. Regression guard: with gateway/config.py stashed out, the two bridge tests fail with tg_cfg is None/slack_cfg is None; re-applied, they pass.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run focused tests for the touched code and all pass (87/87 + 72/72 adjacent)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15.x

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A, key already documented from feat(gateway): per-platform gateway_restart_notification flag #20892
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — config-bridge logic is platform-independent
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Related / Positioning

Audited siblings: the bridge in load_gateway_config() follows the enabled/gateway_restart_notification pattern symmetrically across all top-level platform sections (telegram/slack/discord/wecom/feishu/dingtalk/whatsapp/signal/teams). No widening needed — the same code path handles all platforms uniformly.

Copilot AI review requested due to automatic review settings May 13, 2026 00:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a configuration-loading bug in the Gateway: the per-platform gateway_restart_notification flag (set under top-level platform sections like telegram: in config.yaml) was not being bridged into PlatformConfig, so restart/shutdown notification suppression in gateway/run.py never activated.

Changes:

  • Bridge gateway_restart_notification from top-level config.yaml platform sections into the per-platform config dict as a first-class PlatformConfig field (not under extra).
  • Prevent the top-level platform section from being dropped by the early-continue when gateway_restart_notification is the only key present.
  • Add regression tests covering: bridged false, default true when absent, and the “only-key-in-section” edge case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
gateway/config.py Updates load_gateway_config() bridging logic so gateway_restart_notification reaches PlatformConfig as a top-level field and isn’t skipped by the early-continue.
tests/gateway/test_config.py Adds regression tests ensuring the flag is bridged correctly, defaults correctly when absent, and works when it’s the only setting in a platform section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles labels May 13, 2026
@briandevans briandevans force-pushed the fix/gateway-restart-notification-bridge-24644 branch from 452999e to 1b549ce Compare May 15, 2026 15:14
@briandevans

Copy link
Copy Markdown
Contributor Author

CI audit — all 7 test failures are pre-existing baselines on clean origin/main (9fb40e6a3). Zero failures are in touched code (gateway/platform_config.py, tests/gateway/test_platform_config.py).

Test Symptom Root cause on main
test_provider_parity.py::TestDeveloperRoleSwap::test_developer_role_via_nous_portal ValueError: Model has a context window of 15,000 tokens, which is below the minimum 64,000 Test constructs AIAgent(provider="nous", base_url="https://inference-api.nousresearch.com/v1") — the unmocked context-length probe caches 15,000 and trips the new 64K minimum guard in run_agent.py:2349.
test_provider_parity.py::TestBuildApiKwargsNousPortal::test_includes_nous_product_tags same same
test_provider_parity.py::TestBuildApiKwargsNousPortal::test_uses_chat_completions_format same same
test_discord_adapter.py::TestMentionStrippedCommandDispatch::test_mention_then_command TypeError: catching classes that do not inherit from BaseException is not allowed gateway/platforms/discord.py:3730 does except discord.Forbidden:, but the e2e harness stubs discord as a SimpleNamespace, so discord.Forbidden resolves to a non-exception sentinel.
test_discord_adapter.py::TestMentionStrippedCommandDispatch::test_nickname_mention_then_command same same
test_discord_adapter.py::TestMentionStrippedCommandDispatch::test_text_before_command_not_detected same same
test_discord_adapter.py::TestAutoThreadingPreservesCommand::test_command_detected_after_auto_thread same same

All seven reproduce on clean origin/main locally with identical error text.

…atform sections

The `PlatformConfig.gateway_restart_notification` field was correctly
defined, serialized, and parsed by `from_dict()`, but a user setting
`gateway_restart_notification: false` under a top-level platform section
in `config.yaml` (e.g. `telegram:`) never made it into the platform
dict — `load_gateway_config()`'s bridge list copied keys like
`require_mention`, `dm_policy`, `group_policy` etc. into the platforms
data, but had no entry for `gateway_restart_notification`. The flag
therefore always defaulted to True and the suppression checks at
`gateway/run.py:2767`, `:2810`, `:12801`, `:12860` were unreachable
from config.yaml.

Unlike the other bridged keys, `gateway_restart_notification` is a
top-level dataclass field on `PlatformConfig` (read directly as
`platform_cfg.gateway_restart_notification` in gateway/run.py, not via
`cfg.extra.get(...)`). It can't simply be added to the `bridged` dict
that ends up inside `extra` — it has to land at the same level as
`enabled`. Mirror the `enabled` handling:

  - Track `restart_notif_explicit = "gateway_restart_notification" in platform_cfg`
  - Include it in the early-`continue` predicate so the platform entry
    isn't dropped when this is the only key in the section
  - Assign `plat_data["gateway_restart_notification"]` after the
    `plat_data` dict is materialized, before bridged keys are folded
    into `extra`

The raw value is forwarded as-is and coerced by `PlatformConfig.from_dict`
via `_coerce_bool`, so YAML `false`, `"false"`, `0`, etc. all work.

Three regression tests added under `TestLoadGatewayConfig`:

  - `test_bridges_gateway_restart_notification_from_config_yaml` — the
    headline case from NousResearch#24644; also asserts the value is NOT in
    `extra`, since landing there would silently revert the fix.
  - `test_gateway_restart_notification_defaults_true_when_absent` —
    preserves the existing default when only unrelated keys are set.
  - `test_bridges_gateway_restart_notification_when_only_setting` —
    covers the predicate change: a platform section that contains
    only `gateway_restart_notification: false` (no `enabled`, no
    other bridgeable keys) still creates the platform entry instead
    of being skipped by the `not bridged and not enabled_was_explicit`
    early-`continue`.

Regression guard: with the `gateway/config.py` change stashed out, the
two bridge tests fail with `tg_cfg is None` / `slack_cfg is None`;
re-applied, they pass. The defaults-true test passes in both states
(it asserts the existing default path, not the new bridge).

Fixes NousResearch#24644

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briandevans briandevans force-pushed the fix/gateway-restart-notification-bridge-24644 branch from 1b549ce to 5eb8a3a Compare May 19, 2026 02:32
@briandevans

Copy link
Copy Markdown
Contributor Author

Closing — superseded by colin-chang's commit 7a46c6885 ("fix(gateway): bridge gateway_restart_notification from YAML platform sections") which landed on main on 2026-05-18 and covers the same bridge path. Approach differs slightly (their fix reads the value via from_dict from both top-level and extra; mine pushed it to top-level at bridge time), but the user-visible outcome is the same: gateway_restart_notification: false under a platform section now takes effect. Thanks!

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

Labels

area/config Config system, migrations, profiles comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: gateway_restart_notification config setting silently ignored

3 participants