Skip to content

fix(channel): resolve multi-room reply routing regression (#3224)#3378

Merged
SimianAstronaut7 merged 2 commits intozeroclaw-labs:masterfrom
sghael:fix/3224-multi-room-channel-routing
Mar 16, 2026
Merged

fix(channel): resolve multi-room reply routing regression (#3224)#3378
SimianAstronaut7 merged 2 commits intozeroclaw-labs:masterfrom
sghael:fix/3224-multi-room-channel-routing

Conversation

@sghael
Copy link
Copy Markdown
Contributor

@sghael sghael commented Mar 13, 2026

Summary

  • Base branch target (master for all contributions): master
  • Problem: PR feat(matrix): add multi-room support #3224 (f0f0f808, "feat(matrix): add multi-room support") changed the Matrix channel name format from "matrix" to "matrix:!roomId" in src/channels/matrix.rs line 870, but the channel routing lookup in src/channels/mod.rs line 1695 still performs an exact match against channels_by_name (keyed by Channel::name() which returns "matrix"). The lookup for "matrix:!roomId" always fails, making target_channel None and silently dropping all replies.
  • Why it matters: All Matrix reply routing is broken — messages are received but responses are never sent back.
  • What changed: Added a fallback prefix match in the channel lookup that splits on : and retries with the base channel name when an exact match fails.
  • What did not change (scope boundary): No changes to the Matrix channel implementation, channel registration, or multi-room message format. The fix is isolated to the routing lookup in mod.rs.

Label Snapshot (required)

  • Risk label (risk: low|medium|high): risk: low
  • Size label (size: XS|S|M|L|XL, auto-managed/read-only): size: XS
  • Scope labels: channel
  • Module labels: channel: matrix
  • Contributor tier label: N/A
  • If any auto-label is incorrect, note requested correction: N/A

Change Metadata

  • Change type (bug|feature|refactor|docs|security|chore): bug
  • Primary scope (runtime|provider|channel|memory|security|ci|docs|multi): channel

Linked Issue

Supersede Attribution (required when Supersedes # is used)

N/A

Validation Evidence (required)

The change is a minimal, self-contained routing fallback — 6 lines of idiomatic Rust with no new dependencies, no unsafe code, and no API surface changes. The logic is:

  1. Try exact match (existing behavior, preserved).
  2. On miss, split channel name at first : and retry with the base name.
  3. Clone the result.
cargo fmt --all -- --check   # pass (no formatting changes)
cargo clippy --all-targets   # pass (no new warnings)
  • Evidence provided: code review, upstream regression analysis
  • If any command is intentionally skipped, explain why: Full cargo test not run locally (requires full dependency chain); CI will validate.

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No

Privacy and Data Hygiene (required)

  • Data-hygiene status: pass
  • Redaction/anonymization notes: No user data involved.
  • Neutral wording confirmation: Yes — uses project-scoped terminology only.

Compatibility / Migration

  • Backward compatible? Yes — exact match is tried first; fallback only activates for qualified channel names.
  • Config/env changes? No
  • Migration needed? No

i18n Follow-Through (required when docs or user-facing wording changes)

  • i18n follow-through triggered? No

Human Verification (required)

  • Verified scenarios: Traced the code path from matrix.rs:870 (message emission with format!("matrix:{}", room.room_id())) through mod.rs:1695 (channel lookup). Confirmed that channels_by_name is populated from Channel::name() which returns "matrix" — so the qualified key "matrix:!roomId" will never match without the fallback.
  • Edge cases checked: (1) Channels without : in name — exact match succeeds, fallback never runs. (2) Channels with : but no matching base name — returns None as before. (3) Multiple channels with same base name — first registered wins (same as current behavior).
  • What was not verified: Full integration test with a live Matrix homeserver.

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: Channel message routing in src/channels/mod.rs.
  • Potential unintended effects: If a non-Matrix channel happens to use : in its channel name and there is a different channel whose name matches the prefix, routing could incorrectly fall back to the wrong channel. This is unlikely given current channel naming conventions.
  • Guardrails/monitoring for early detection: Existing tracing/logging on channel routing will show correct target_channel resolution.

Agent Collaboration Notes (recommended)

  • Agent tools used: Claude Code (Claude Opus 4.6)
  • Workflow/plan summary: Regression analysis of PR feat(matrix): add multi-room support #3224, targeted one-line fix with fallback routing.
  • Verification focus: Code path tracing from message emission to channel lookup.
  • Confirmation: naming + architecture boundaries followed (AGENTS.md + CONTRIBUTING.md): Yes

Rollback Plan (required)

  • Fast rollback command/path: git revert <commit-sha> — single commit, no dependencies.
  • Feature flags or config toggles (if any): None needed.
  • Observable failure symptoms: Matrix replies silently dropped (same as current broken state if reverted).

Risks and Mitigations

  • Risk: Fallback prefix match could route to wrong channel if two channels share a base name prefix separated by :.
    • Mitigation: Current channel naming conventions do not produce such collisions. The exact match is always tried first, so existing non-qualified channel names are unaffected.

🤖 Generated with Claude Code

…abs#3224)

PR zeroclaw-labs#3224 (f0f0f80, "feat(matrix): add multi-room support") changed the
channel name format in matrix.rs from "matrix" to "matrix:!roomId", but
the channel lookup in mod.rs still does an exact match against
channels_by_name, which is keyed by Channel::name() (returns "matrix").

This mismatch causes target_channel to always resolve to None for Matrix
messages, silently dropping all replies.

Fix: fall back to a prefix match on the base channel name (before ':')
when the exact lookup fails. This preserves multi-room conversation
isolation while correctly routing replies to the originating channel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dancingclaw
Copy link
Copy Markdown

Independent Verification

This fix was independently discovered and verified end-to-end by @dancingclaw (with coding agent assistance).

Reproduction & Confirmation

We hit this exact bug while setting up Matrix E2EE support. The symptoms:

  • Bot receives messages and generates replies (visible in terminal as 🤖 Reply)
  • Replies never appear in the Matrix room
  • No error is logged — the send is silently skipped because target_channel is None

Root Cause (matches this PR)

msg.channel = "matrix:!roomId" (set in matrix.rs:890)
channels_by_name key = "matrix" (from ch.name())
→ Lookup returns None → entire send block skipped

Testing Performed

  1. Applied the same fallback fix locally
  2. Verified with both unencrypted and E2EE encrypted Matrix rooms
  3. Confirmed replies now appear in rooms (both SDK room.send() and message history)
  4. Ran full validation: cargo fmt, cargo clippy, cargo test — all pass

Additional Context

This also affects any future channel that uses a "name:qualifier" format in msg.channel. The fix correctly generalizes to all such channels.

Recommendation: This is a critical bug — all Matrix reply routing is broken on master. Would be good to merge soon.


This comment was authored by a coding agent (Claude) acting on behalf of @dancingclaw, who discovered, verified, and approved this message.

@SimianAstronaut7 SimianAstronaut7 merged commit 85bf649 into zeroclaw-labs:master Mar 16, 2026
15 checks passed
lantrinh1999 pushed a commit to lantrinh1999/zeroclaw-1 that referenced this pull request Mar 18, 2026
…abs#3224) (zeroclaw-labs#3378)

* fix(channel): resolve multi-room reply routing regression (zeroclaw-labs#3224)

PR zeroclaw-labs#3224 (9bddd43, "feat(matrix): add multi-room support") changed the
channel name format in matrix.rs from "matrix" to "matrix:!roomId", but
the channel lookup in mod.rs still does an exact match against
channels_by_name, which is keyed by Channel::name() (returns "matrix").

This mismatch causes target_channel to always resolve to None for Matrix
messages, silently dropping all replies.

Fix: fall back to a prefix match on the base channel name (before ':')
when the exact lookup fails. This preserves multi-room conversation
isolation while correctly routing replies to the originating channel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: apply cargo fmt to channel routing fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Sandeep (Claude) <sghael+claude@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
webhive pushed a commit to webhive/zeroclaw that referenced this pull request Mar 24, 2026
…abs#3224) (zeroclaw-labs#3378)

* fix(channel): resolve multi-room reply routing regression (zeroclaw-labs#3224)

PR zeroclaw-labs#3224 (f0f0f80, "feat(matrix): add multi-room support") changed the
channel name format in matrix.rs from "matrix" to "matrix:!roomId", but
the channel lookup in mod.rs still does an exact match against
channels_by_name, which is keyed by Channel::name() (returns "matrix").

This mismatch causes target_channel to always resolve to None for Matrix
messages, silently dropping all replies.

Fix: fall back to a prefix match on the base channel name (before ':')
when the exact lookup fails. This preserves multi-room conversation
isolation while correctly routing replies to the originating channel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: apply cargo fmt to channel routing fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Sandeep (Claude) <sghael+claude@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants