fix(gateway): skip credential-less WhatsApp on secondary multiplex profiles

_start_one_profile_adapters skipped only Platform.RELAY as shared
process-level ingress. WhatsApp is the same shape: the bridge is one
authenticated session tied to a single phone number, so a secondary
profile has no credential of its own to bring; constructing an adapter
for it only produced a connect/retry loop that stalled startup for every
profile queued behind it. Treat WhatsApp like Relay -- the active profile
owns the connection and route-stamped source.profile fans inbound turns
out to secondary profiles.

Salvage of #69042 (narrowed by its author to this one behavioral line);
test re-expressed on the current secondary-startup fixtures.

Co-authored-by: sshawn <28279366+lsshawn@users.noreply.github.com>
This commit is contained in:
Teknium
2026-09-02 03:41:57 -07:00
parent 4fa1d5498e
commit febd2af391
2 changed files with 46 additions and 4 deletions
+9 -4
View File
@@ -17146,12 +17146,17 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
platform.value,
)
continue
# Relay is shared process-level ingress in multiplex mode. The
# active profile owns the one connection; connector-stamped
# source.profile routes inbound turns to secondary profiles.
# Relay and WhatsApp are shared process-level ingress in multiplex
# mode: one connection owned by the active profile, with
# route-stamped source.profile fanning inbound turns out to
# secondary profiles. The WhatsApp bridge is a single authenticated
# session tied to one phone number -- a secondary profile has no
# credential of its own to bring, so constructing an adapter for it
# only yields a connect/retry loop that stalls startup for every
# profile queued behind it.
if (
getattr(self.config, "multiplex_profiles", False)
and platform is Platform.RELAY
and platform in (Platform.RELAY, Platform.WHATSAPP)
):
continue
try:
@@ -1034,6 +1034,43 @@ class TestSecondaryProfileConfigHandling:
assert "reviewer" in str(exc_info.value)
assert "reviewer" not in runner._profile_adapters
@pytest.mark.asyncio
async def test_secondary_profile_adapter_start_skips_whatsapp(self, monkeypatch):
"""WhatsApp is shared process-level ingress like Relay: the bridge is
one authenticated session tied to a single phone number, so a
credential-less secondary profile must be skipped (not stall startup
in a connect/retry loop) while its other platforms start normally."""
runner = _secondary_recovery_runner()
direct = _SecondaryRecoveryAdapter()
_install_secondary_reconnect_context(monkeypatch, runner, direct)
monkeypatch.setattr(
"gateway.config.load_gateway_config",
lambda: GatewayConfig(
multiplex_profiles=True,
platforms={
Platform.WHATSAPP: PlatformConfig(enabled=True),
Platform.DISCORD: PlatformConfig(enabled=True, token="profile-token"),
},
),
)
factory_calls = []
def _create_adapter(platform, config):
factory_calls.append(platform)
return direct
async def _connect(adapter, platform):
return True
monkeypatch.setattr(runner, "_create_adapter", _create_adapter)
monkeypatch.setattr(runner, "_connect_initial_adapter_with_timeout", _connect)
connected = await runner._start_one_profile_adapters("clientbot", "/tmp/x", {})
assert connected == 1
assert factory_calls == [Platform.DISCORD]
assert runner._profile_adapters["clientbot"] == {Platform.DISCORD: direct}
class TestSecondaryProfileHookRegistration:
"""A secondary profile's own `hooks:` block must register on ITS