Skip to content

refactor(gateway): extract GatewayDispatchMixin from run.py (slice 26 of #54962) - #77756

Open
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:refactor/gateway-mixin-dispatch
Open

refactor(gateway): extract GatewayDispatchMixin from run.py (slice 26 of #54962)#77756
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:refactor/gateway-mixin-dispatch

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Related #54962 #55138

What / Why

Extracts the message-dispatch / event-routing core of GatewayRunner out of
gateway/run.py (26,823 → 21,948 lines) into a new
gateway/dispatch_mixin.py module, following the established mixin pattern
(gateway/authz_mixin.py, gateway/kanban_watchers.py,
gateway/slash_commands.py). This is the largest mixin of the
GatewayRunner decomposition (gate 4 of #54962): 28 methods / 4,812 lines,
including the inbound pipeline (_handle_message,
_handle_message_with_agent), the busy-session slash commands
(/stop, /new, /queue, /steer, /goal, ...), the active-session-slot admission
control, and the destructive-slash confirmation gates.

GatewayDispatchMixin is added first in GatewayRunner's bases. Every
method is moved verbatim (byte-identical, verified against HEAD~1 by an
in-memory splice reconstruction); self.* calls resolve unchanged via the MRO.

Behavior-neutral mechanics:

  • Module-level run.py helpers (_hermes_home, _load_gateway_config,
    _AGENT_PENDING_SENTINEL, _float_env, ...) are imported lazily inside
    the method that uses them
    (from gateway.run import ... at call time),
    so gateway/dispatch_mixin.py never imports gateway.run at module level —
    no import cycle.
  • logger = logging.getLogger("gateway.run") at module top preserves the
    original log-record name.
  • No module-level from gateway.run import in the new module; run.py keeps
    its own definitions of those helpers.

How to test

# import + MRO smoke
python -c "import gateway.run; assert gateway.run.GatewayRunner._handle_message.__module__ == 'gateway.dispatch_mixin'"

# targeted suites (106 tests, all passing)
python -m pytest tests/gateway/test_gateway_command_dispatch_minimal.py \
  tests/gateway/test_busy_session_ack.py tests/gateway/test_busy_session_auth_bypass.py \
  tests/gateway/test_compression_in_flight_check.py tests/gateway/test_max_concurrent_sessions.py \
  tests/gateway/test_destructive_slash_confirm.py tests/gateway/test_subagent_protection_30170.py \
  tests/gateway/test_mcp_reload_refreshes_cached_agents.py tests/gateway/test_35994_reset_button_deadlock.py \
  tests/gateway/test_fresh_reset_skill_injection.py tests/gateway/test_stale_self_heal_agent_cache_eviction.py \
  tests/gateway/test_restart_resume_pending.py tests/gateway/test_internal_event_bypass_pairing.py \
  tests/gateway/test_internal_event_never_interrupts_busy_session.py \
  tests/gateway/test_10710_auto_reset_evicts_cached_agent.py \
  tests/hermes_cli/test_destructive_slash_confirm_gate.py tests/gateway/test_session_race_guard.py \
  tests/gateway/test_tool_response_drop_recovery.py tests/gateway/test_35809_auto_reset_clean_context.py \
  tests/gateway/test_48031_model_switch_after_auto_reset.py \
  tests/gateway/test_compression_deferred_soft_result.py \
  tests/gateway/test_compression_session_id_persistence.py -q --no-header -p no:cacheprovider

Verification receipts: git diff --check clean; scripts/check-windows-footguns.py
clean; byte-verbatim move check 28/28 methods; post-commit in-memory splice
reconstruction vs HEAD~1 byte-identical.

Platforms tested

  • Windows (worktree build): import smoke + 106 targeted tests green.

Scope notes (honest)

  • Test updates (5 files): the AST-pin regression tests
    (test_10710, test_35809, test_48031, test_compression_deferred_soft_result,
    test_compression_session_id_persistence) parse gateway/run.py source to pin
    fingerprints that live inside _handle_message_with_agent. They now parse
    gateway.dispatch_mixin instead (same pattern the codebase already uses for
    gateway/slash_commands.py in test_48031). Pin logic and assertions are
    unchanged — only the module whose source is inspected moved.
  • Lazy imports added inside 9 method bodies: the only textual additions to
    the moved code are deferred from gateway.run import ... lines at the top of
    the 9 methods that reference run.py module-level helpers (listed in the new
    module's docstring). This is the documented sibling-mixin pattern.
  • All 28 partition methods moved; nothing left behind. run.py still imports
    GatewayDispatchMixin for the class MRO; all moved names remain reachable
    as gateway.run.GatewayRunner.<name> and via from gateway.run import <name>
    test monkeypatches.

Part of #54962
Part of #55138

Part of #78207
Part of #78647
Part of #78791

… of NousResearch#54962)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
@andrexibiza
andrexibiza force-pushed the refactor/gateway-mixin-dispatch branch from eff2f78 to 01e2be2 Compare August 3, 2026 14:30
@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Aug 3, 2026
This was referenced Aug 4, 2026
This was referenced Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants