Skip to content

fix(matrix): isolate room context and restore reliable inbound dispatch#18505

Merged
austinpickett merged 11 commits into
NousResearch:mainfrom
nepenth:feature/matrix-foundation
Jun 11, 2026
Merged

fix(matrix): isolate room context and restore reliable inbound dispatch#18505
austinpickett merged 11 commits into
NousResearch:mainfrom
nepenth:feature/matrix-foundation

Conversation

@nepenth

@nepenth nepenth commented May 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked PR 1/3 for the Matrix gateway parity series.

This PR focuses on Matrix foundation correctness and safety:

  • room identity and DM classification (MatrixRoomIdentity, m.direct authority)
  • project-room context isolation (Project A / Project B boundaries)
  • MATRIX_SESSION_SCOPE=auto|room|thread
  • Matrix room-boundary system-prompt injection
  • /resume same-room safety (fail-closed; --cross-room / --all escape hatches)
  • Matrix scope visibility in /status
  • inbound sync hardening layered atop upstream handle_sync() dispatch

Follow-up PRs in this stack add Matrix-native tools/interactions (#18506) and then rendering/media/E2EE/diagnostics/docs (#18507).

Note vs current main: upstream already ships handle_sync() inbound dispatch (#7914). This PR’s primary merge value is the room/session isolation class of bugs, not re-claiming inbound dispatch alone. Aligns with the handle_sync approach (supersedes closed #37807 client.start() proposal).

Why

Matrix project rooms need stable room/session boundaries. A named two-member room such as "Project - Project B" should not be treated as a DM unless Matrix m.direct account data says it is direct. A message inside Project B also must not silently resume or act on Project A context.

Changes

Matrix adapter

  • Adds MatrixRoomIdentity with m.direct-aware DM classification (named rooms win over stale account data).
  • Adds room metadata to SessionSource.
  • Adds MATRIX_SESSION_SCOPE=auto|room|thread.
  • Hardens inbound filtering, duplicate/startup-event behavior, and sync dispatch integration.

Gateway/session

  • Adds Matrix room-boundary context to the system prompt.
  • Ports Matrix /resume same-room guard and /status scope output into gateway/slash_commands.py (upstream mixin decomposition).
  • Preserves upstream role_authorized and DM user_id isolation fixes.

Tests

  • Adds Project A / Project B context-isolation tests.
  • Adds full inbound handler-path coverage.
  • Adds concurrent task-local context coverage.
  • Adds quoted /resume and cross-room resume tests.
  • i18n catalog parity for Matrix slash-command keys (all locales).

Security / privacy

  • No Honcho or memory-provider behavior is changed.
  • Cross-room resume is blocked by default.
  • Room metadata is used for routing and prompt clarity only.

Validation

  • .venv/bin/python -m py_compile gateway/platforms/matrix.py gateway/run.py gateway/session.py gateway/slash_commands.py
  • .venv/bin/python -m pytest tests/gateway/test_matrix.py tests/gateway/test_matrix_project_context_isolation.py tests/gateway/test_matrix_exec_approval.py tests/gateway/test_matrix_approval_reaction_fail_closed.py tests/agent/test_i18n.py -q377 passed
  • git diff --check
  • Rebased on main @ a72bb037 (Jun 10, 2026); branch head 3a9439c19

Stack

Supersedes #17702 by incorporating named-room identity preservation into a broader Matrix foundation PR with project-room isolation.

Related to #7914 (Matrix inbound sync/dispatch hardening on top of upstream handle_sync).

AgentFirst metadata (#23815) is intentionally out of scope (fork-only).

@nepenth

nepenth commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

May 7, 2026 Update

This PR has been refreshed after the v2026.5.7 release.

Branch Refresh

  • Rebased onto current origin/main.
  • Current head: 7acf68fa1.
  • Base head observed during refresh: 04193cf71.

Follow-Up Fix

  • Aligned the Matrix approval-reaction test expectation with the current reaction set.
  • The foundation branch already carried the current Matrix approval behavior; the inherited test still expected the older two-reaction seed set.

Local Validation

Passed:

scripts/run_tests.sh \
  tests/gateway/test_matrix.py \
  tests/gateway/test_matrix_project_context_isolation.py \
  tests/gateway/test_matrix_exec_approval.py \
  tests/gateway/test_matrix_voice.py \
  -q -o 'addopts='

Result:

225 passed, 1 skipped

Also passed:

python -m py_compile \
  gateway/platforms/matrix.py \
  gateway/run.py \
  gateway/session.py

git diff --check

@nepenth nepenth force-pushed the feature/matrix-foundation branch 3 times, most recently from 4c9eef2 to 30b6f89 Compare May 14, 2026 12:54
@nepenth

nepenth commented May 14, 2026

Copy link
Copy Markdown
Contributor Author

History cleanup

Rebased and cleaned commit history for reviewability.

  • No scope change.
  • Final tree is equivalent to the previous branch state.
  • Focused Matrix validation still passes locally: 297 passed.

@nepenth nepenth force-pushed the feature/matrix-foundation branch from 30b6f89 to 86279e4 Compare May 16, 2026 19:04
@nepenth

nepenth commented May 16, 2026

Copy link
Copy Markdown
Contributor Author

v2026.5.16 refresh

Rebased this Matrix PR stack onto current upstream main after the v2026.5.16 / v0.14.0 release.

Notes:

  • The v2026.5.16 release did not include this Matrix parity work; these PRs remain open and unmerged.
  • Checked Matrix/gateway-adjacent upstream changes from the release window.
  • Focused Matrix validation passed locally: 323 passed.
  • py_compile and git diff --check passed.

@nepenth

nepenth commented May 16, 2026

Copy link
Copy Markdown
Contributor Author

Review focus after v2026.5.16 refresh

This remains the intended first merge target for the Matrix series. I marked the follow-up PRs (#18506, #18507, and #23815) as draft stacked previews so this PR can be reviewed independently.

Scope in this PR

  • Reliable Matrix inbound dispatch.
  • Named-room identity and DM classification fixes.
  • Project-room context isolation.
  • MATRIX_SESSION_SCOPE=auto|room|thread.
  • Matrix /resume same-room guard and Matrix /status scope output.

Out of scope

Current validation

  • Rebased on current main after v2026.5.16.
  • Focused Matrix/gateway validation passed locally: 323 tests.

I can split this further if maintainers prefer, but this is the smallest unit that fully fixes the room/session isolation class of bugs.

@nepenth nepenth force-pushed the feature/matrix-foundation branch 2 times, most recently from d8bb64a to e84fef4 Compare May 30, 2026 20:06
@nepenth nepenth requested a review from a team May 30, 2026 20:06
@nepenth nepenth force-pushed the feature/matrix-foundation branch 2 times, most recently from 2dd2639 to b16b54b Compare June 2, 2026 01:12
@nepenth nepenth force-pushed the feature/matrix-foundation branch from 01371be to 3cd9dfe Compare June 6, 2026 13:19
@nepenth

nepenth commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased this PR onto current main (56236b16e) after the v0.16.0 / v2026.6.5 release.

Notes:

  • Preserved upstream Matrix !command alias behavior.
  • Kept the current lazy dependency / Markdown dependency model from main.
  • Fixed Matrix test isolation around mautrix stubs and lazy-dependency requirements.
  • CI is green after the rebase.

The custom Synapse/AgentFirst metadata PR (#23815) has been retired from the active stack to keep this series focused on upstreamable core Matrix functionality.

nepenth added 4 commits June 10, 2026 09:41
Move Matrix /status scope output and /resume same-room guards from the
pre-refactor gateway/run.py into gateway/slash_commands.py so PR NousResearch#18505
foundation behavior survives the upstream god-file decomposition.

Uses i18n keys for Matrix resume/status messages. Preserves upstream
session.py fixes (role_authorized, DM user_id isolation).
@nepenth nepenth force-pushed the feature/matrix-foundation branch from 3cd9dfe to 9604a6b Compare June 10, 2026 13:44
nepenth added 2 commits June 10, 2026 09:45
Document why Hermes uses an explicit sync loop with handle_sync() rather than
client.start(), aligning with upstream NousResearch#7914 diagnostics while preserving
Hermes background maintenance tasks.
The Matrix /resume and /status slash-command keys added in the foundation
PR must exist in every supported locale file. tests/agent/test_i18n.py
asserts key and placeholder parity across catalogs.

Non-English locales use English strings as interim placeholders until
community translators can localize them.
@nepenth

nepenth commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Jun 10, 2026 rebase refresh (post–a72bb037 / v2026.6.x window)

Rebased the full Matrix stack onto current upstream main and force-pushed the refreshed branches.

Item Value
Base (main) a72bb037
Branch head 3a9439c19
Commits ahead of main 8
Mergeable clean (no conflicts)

Rebase highlights

  • Gateway mixin port: Matrix /status scope output and /resume same-room guards now live in gateway/slash_commands.py, matching the upstream god-file decomposition. Upstream session fixes (role_authorized, DM user_id isolation) are preserved.
  • Scope reframing vs current main: upstream already ships handle_sync() inbound dispatch (Matrix gateway can send messages but doesn't receive/respond to incoming messages #7914 lineage; supersedes the closed client.start() approach in fix(gateway): start Matrix mautrix syncer #37807). This PR’s primary merge value is room/session isolationMatrixRoomIdentity, MATRIX_SESSION_SCOPE, project-room context boundaries, and fail-closed /resume — not re-claiming inbound dispatch alone.
  • i18n parity fix: added the new Matrix /resume + /status catalog keys to all 15 non-English locale files (3a9439c19). tests/agent/test_i18n.py now passes (47/47).
  • AgentFirst (feat(matrix): add optional AgentFirstModule metadata support #23815) remains out of scope — fork-only metadata integration.

Local validation (.venv, Jun 10 2026)

.venv/bin/python -m py_compile \
  gateway/platforms/matrix.py gateway/run.py gateway/session.py gateway/slash_commands.py
git diff --check origin/main...feature/matrix-foundation

.venv/bin/python -m pytest \
  tests/gateway/test_matrix.py \
  tests/gateway/test_matrix_project_context_isolation.py \
  tests/gateway/test_matrix_exec_approval.py \
  tests/gateway/test_matrix_approval_reaction_fail_closed.py \
  tests/agent/test_i18n.py -q

Result: 377 passed (Matrix + i18n subset)

Suggested review focus (PR 1/3)

  1. MatrixRoomIdentity / m.direct vs named-room DM classification
  2. MATRIX_SESSION_SCOPE=auto|room|thread session-key behavior
  3. Project A ↔ Project B isolation tests (test_matrix_project_context_isolation.py)
  4. Matrix /resume same-room guard + --cross-room / --all escape hatches in slash_commands.py
  5. Matrix scope block in /status

PRs #18506 and #18507 remain draft stacked previews until this foundation PR lands. Happy to split further if maintainers prefer a smaller first merge unit.

austinpickett
austinpickett previously approved these changes Jun 10, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review: APPROVE (still valid — no rebase needed)

Reviewed core logic only (gateway/platforms/matrix.py, session.py, config.py, slash_commands.py); skipped the 16 locales/*.yaml + 2 website/docs files as i18n/docs noise.

Currency / mergeability

Despite the May-01 open date, this is current: head 3a9439c is rebased onto a72bb037, which is the present origin/main tip (0 commits between merge-base and main). GitHub reports MERGEABLE. No rebase required.

Core claim verified — room-isolation bug is still present on main

The 'isolate room context' fix is not superseded. On current main:

  • DM misclassification still live: get_chat_info uses chat_type = "dm" if await self._is_dm_room(chat_id) else "group" — purely _dm_rooms/m.direct cache, with no named-room override. A named two-member project room can still be misclassified as a DM.
  • No MATRIX_SESSION_SCOPE (auto|room|thread) on main.
  • No MatrixRoomIdentity / is_direct_account_data authority logic.
  • No cross-room /resume guard in slash_commands.py (no same-room/--cross-room/--all handling).

This PR adds exactly these: has_explicit_name so named rooms win over stale m.direct; scope routing at matrix.py ~L1529-1541; room-boundary system-prompt injection in session.py (build_session_context_prompt); fail-closed cross-room resume. The PR body's own framing is accurate — it does not re-claim handle_sync() inbound dispatch (already on main via #7914); merge value is the room/session-isolation class of bugs.

Notes / non-blocking

  1. matrix.py is +1499/-290 in this single file — large but cohesive (new class + scope plumbing). Recommend the stack (#18506/#18507) stays draft until this lands to avoid re-rebasing churn.
  2. session.py change correctly honors redact_pii (_hash_chat_id) for room/thread IDs in the prompt — good.

Verdict: APPROVE. Bug confirmed still live on main, change is well-scoped, branch is current and mergeable.

…quire_mention

Revert the early MATRIX_ALLOWED_USERS gate in _on_room_message so inbound
sender authorization stays in gateway authz like main. Parse require_mention
from config.extra (platforms.matrix / top-level matrix yaml) with env fallback,
matching thread_require_mention and fixing Forge when require_mention is set
only in profile config.yaml.
@nepenth

nepenth commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Pushed two narrowly scoped fixes on feature/matrix-foundation (cherry-picked to the stacked branches):

  1. Revert early MATRIX_ALLOWED_USERS gate in _on_room_message — inbound sender authorization is back on the gateway authz_mixin path like main. The adapter no longer drops messages at intake based on allowed_users; room whitelist and ignore-pattern gates are unchanged.

  2. require_mention reads config.extraplatforms.matrix.require_mention / top-level matrix.require_mention in profile config.yaml is now honored (with MATRIX_REQUIRE_MENTION env fallback), matching thread_require_mention and free_response_rooms.

Commit: 40d277ada on foundation; tests cover config.extra parsing and deferred allowed-users authz.

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review Summary

PR #18505 — fix(matrix): isolate room context and restore reliable inbound dispatch
Priority: P1
Verdict: Request Changes


Critical

** exposes full — potential PII / info-leak in shared rooms**
In gateway/slash_commands.py, the new Matrix scope block appends:

t("gateway.status.matrix_scope_key", session_key=session_key)

Session keys typically encode the room ID and/or user ID. Exposing the raw key in a shared/non-DM Matrix room leaks routing metadata to all room participants. Either truncate (e.g. first 8 chars + ) or gate this line on the DM/admin context check that guards other sensitive /status fields.


Warnings

1. Type annotation mismatch on _MatrixModelPickerPrompt.bot_reaction_events

@dataclass
class _MatrixModelPickerPrompt:
    bot_reaction_events: dict[str, str] = None   # ← annotation says dict, default is None

The __post_init__ guard correctly initialises the field at runtime, but type checkers (mypy/pyright) will flag this as dict[str, str] | None conflict on the annotation. Fix: either use field(default_factory=dict) (cleanest) or annotate as dict[str, str] | None = None.

2. PR scope significantly exceeds the stated P1 bug fix — increases review risk
The 2159-line diff bundles code clearly belonging to PR 2/3 and PR 3/3:

  • _MatrixHtmlSanitizer (~70 lines) — Matrix rendering safety; PR 3/3's own scope per the stack note
  • _MatrixModelPickerPrompt + _MATRIX_MODEL_PICKER_REACTIONS — reaction/model-picker UI; PR 2/3 scope
  • _MATRIX_CAPABILITIES dict + get_matrix_capabilities() — capability registry; informational feature
  • Env var stubs for PR 2/3 tools: MATRIX_TOOLS_ALLOW_REDACTION, MATRIX_TOOLS_ALLOW_INVITES, MATRIX_TOOLS_ALLOW_ROOM_CREATE, MATRIX_ALLOW_PUBLIC_ROOMS

These are additive, non-breaking additions, but they enlarge the blast radius of a P1 merge and make it harder to bisect if a regression surfaces. Recommend extracting them — or at minimum tag the bundled items explicitly in the PR description so merge reviewers know what can be reverted independently.

3. Non-English locale files contain untranslated English strings
All 15 non-English locales (af, de, es, fr, ga, hu, it, ja, ko, pt, ru, tr, uk, zh-hant, zh) appear to carry the new Matrix-specific keys verbatim in English (including the full message strings, not just placeholders). This is acceptable for a P1 merge but should be tracked as a follow-up localization task.

4. _gateway_session_origin_for_id accesses _entries (private attribute)

entries = getattr(self.session_store, "_entries", {}) or {}

Fragile coupling to session store internals. If _entries is renamed or restructured the cross-room guard silently returns None and the room boundary check degrades to allow_cross_room=False behavior (which is fail-closed, so no safety regression — but it would silently break UX). Consider exposing a public lookup_by_session_id() method on the session store.


Looks Good

Core room isolation: MatrixRoomIdentity + m.direct DM classification
The named-room-wins-over-stale-account-data logic in _resolve_room_identity() is correct. conflict=True when is_direct and has_explicit_name is a good diagnostic flag. Cache invalidation on _refresh_dm_cache() (clearing both dicts) is properly placed.

_dispatch_sync handles both sync and async handle_sync()

tasks = client.handle_sync(sync_data)
if inspect.isawaitable(tasks):
    tasks = await tasks
if tasks:
    await asyncio.gather(*tasks)

Correct. The inspect.isawaitable guard future-proofs against upstream API changes and the gather correctly fans out concurrent task execution.

/resume same-room guard is fail-closed
Cross-room resume requires explicit --cross-room or --all flags. Default behavior blocks. shlex.split for argument parsing is the right call here (handles quoted session names with spaces). The --all and --cross-room escape hatches are well-scoped.

config.py cleans up duplicate allowed_rooms registration
Old code had free_response_rooms AND a duplicate ar block both reaching MATRIX_ALLOWED_ROOMS. The refactor deduplicates correctly and adds the missing allowed_users YAML-to-env mapping.

i18n catalog parity (all 16 locales updated)
All locale files carry the new Matrix-specific slash-command strings in lockstep. No locale is left missing keys that would cause t() fallback failures at runtime.

Room identity LRU-style eviction is correct for the scale
O(n) eviction at 256-entry capacity is negligible. The oldest-timestamp eviction is correct and deterministic.


Reviewed by Hermes Agent

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review Summary

PR #18505 — fix(matrix): isolate room context and restore reliable inbound dispatch
Priority: P1
Verdict: Request Changes


Critical

/status exposes full session_key — potential PII / info-leak in shared rooms
In gateway/slash_commands.py, the new Matrix scope block appends:

t("gateway.status.matrix_scope_key", session_key=session_key)

Session keys typically encode the room ID and/or user ID. Exposing the raw key in a shared/non-DM Matrix room leaks routing metadata to all room participants. Either truncate (e.g. first 8 chars + ) or gate this line on the DM/admin context check that guards other sensitive /status fields.


Warnings

1. Type annotation mismatch on _MatrixModelPickerPrompt.bot_reaction_events

@dataclass
class _MatrixModelPickerPrompt:
    bot_reaction_events: dict[str, str] = None   # annotation says dict, default is None

The __post_init__ guard correctly initialises the field at runtime, but mypy/pyright will flag this as a dict[str, str] | None conflict. Fix: use field(default_factory=dict) (cleanest) or annotate as dict[str, str] | None = None.

2. PR scope significantly exceeds the stated P1 bug fix — increases review risk
The 2159-line diff bundles code clearly belonging to PR 2/3 and PR 3/3:

  • _MatrixHtmlSanitizer (~70 lines) — Matrix rendering safety; PR 3/3 scope per the stack note
  • _MatrixModelPickerPrompt + _MATRIX_MODEL_PICKER_REACTIONS — reaction/model-picker UI; PR 2/3 scope
  • _MATRIX_CAPABILITIES dict + get_matrix_capabilities() — capability registry
  • Env var stubs for PR 2/3 tools: MATRIX_TOOLS_ALLOW_REDACTION, MATRIX_TOOLS_ALLOW_INVITES, MATRIX_TOOLS_ALLOW_ROOM_CREATE, MATRIX_ALLOW_PUBLIC_ROOMS

These are additive/non-breaking, but they enlarge the blast radius of a P1 merge and make it harder to bisect if a regression surfaces. Recommend extracting them or explicitly tagging them in the PR description so merge reviewers know what can be reverted independently.

3. Non-English locale files contain untranslated English strings
All 15 non-English locales (af, de, es, fr, ga, hu, it, ja, ko, pt, ru, tr, uk, zh-hant, zh) carry the new Matrix-specific keys verbatim in English. Acceptable for a P1 merge but should be tracked as a follow-up i18n task.

4. _gateway_session_origin_for_id accesses _entries (private attribute)

entries = getattr(self.session_store, "_entries", {}) or {}

Fragile coupling to session store internals. If _entries is renamed, the same-room guard silently degrades to fail-closed behavior (safe but silently broken UX). Consider exposing a public lookup_by_session_id() on the session store.


Looks Good

Core room isolation: MatrixRoomIdentity + m.direct DM classification
Named-room-wins-over-stale-account-data logic in _resolve_room_identity() is correct. conflict=True when is_direct and has_explicit_name is a good diagnostic flag. Cache invalidation on _refresh_dm_cache() is correctly placed.

_dispatch_sync handles both sync and async handle_sync()

tasks = client.handle_sync(sync_data)
if inspect.isawaitable(tasks):
    tasks = await tasks
if tasks:
    await asyncio.gather(*tasks)

Correct. The inspect.isawaitable guard future-proofs against upstream API changes; gather correctly fans out concurrent task execution.

/resume same-room guard is fail-closed
Cross-room resume requires explicit --cross-room or --all flags. shlex.split for argument parsing correctly handles quoted session names with spaces. The escape hatches are well-scoped.

config.py cleans up duplicate allowed_rooms registration
Old code had a duplicate ar block both reaching MATRIX_ALLOWED_ROOMS. The refactor deduplicates correctly and adds the missing allowed_users YAML-to-env mapping.

i18n catalog parity (all 16 locales updated)
All locale files carry the new Matrix-specific keys in lockstep — no locale is left missing keys that would cause t() fallback failures at runtime.

Room identity LRU-style eviction is correct for the scale
O(n) eviction at 256-entry capacity is negligible. Oldest-timestamp eviction is correct and deterministic.


Reviewed by Hermes Agent

@austinpickett

Copy link
Copy Markdown
Collaborator

Hermes Agent automated review summary:

2159-line diff across 26 files reviewed. One critical issue: the new /status Matrix scope block emits the raw session_key to all room participants — this should be truncated or gated on DM/admin context before merge.

Two structural warnings: (1) _MatrixModelPickerPrompt.bot_reaction_events carries a dict[str,str] = None annotation/default mismatch that will fail strict type checking; use field(default_factory=dict) instead. (2) The diff bundles _MatrixHtmlSanitizer, model-picker plumbing, and capability stubs that per the author's own stack note belong to PRs 2/3 and 3/3 — not blocking but elevates surface area for a P1.

Core room isolation work (MatrixRoomIdentity, m.direct DM classification, _dispatch_sync hardening, fail-closed /resume same-room guard, config.py de-duplication) is solid. Full review posted above. 🤖

@nepenth

nepenth commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Jun 11 follow-up: review fixes pushed

Addressed the concrete review items and refreshed this foundation layer:

  • Matrix /status no longer emits the raw session_key; it now shows a stable sha256:<12> fingerprint so room/user-derived routing metadata is not exposed in shared rooms.
  • _MatrixModelPickerPrompt.bot_reaction_events now uses field(default_factory=dict).
  • Matrix /resume room-scope lookup now uses a public SessionStore.lookup_by_session_id() helper instead of reaching directly into _entries.
  • MATRIX_ALLOWED_ROOMS still constrains shared rooms, but Matrix DMs bypass that gate so private chats continue to work when operators use a room allowlist.
  • Re-cleaned stack ancestry so feat(matrix): add scoped native tools and reaction controls #18506 is based on this branch without duplicated lower-layer commits.

Validation:

  • Local top-stack Matrix suite: 354 passed, 1 skipped.
  • GitHub CI is green on this PR.

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All three blockers from my last review are resolved:

  • /status session_key leak — closed. The _session_key_fingerprint() helper now emits sha256:<12> and the test explicitly asserts session_key not in result and the truncated prefix is absent too. ✓
  • bot_reaction_events annotation/default mismatch — fixed with field(default_factory=dict). ✓
  • /resume reaching into _entries — replaced with the public SessionStore.lookup_by_session_id() helper, with a fallback for test doubles. ✓

354 passed, 1 skipped on the local Matrix suite; CI green. Approving the foundation layer — stacked PRs 2/3 and 3/3 can proceed.

@austinpickett austinpickett merged commit 4717989 into NousResearch:main Jun 11, 2026
29 checks passed
@zodiacg

zodiacg commented Jun 12, 2026

Copy link
Copy Markdown

The _MatrixHtmlSanitizer is too aggressive — it strips <table>, <tr>, <td>, <th>, <thead>, <tbody> tags even though Element renders them fine. The markdown library correctly generates table HTML (via the tables extension which was already enabled before this PR), so these tags should be in _ALLOWED_TAGS.

Before #18505, _markdown_to_html() returned the markdown library output directly with no sanitizer — all standard HTML (including tables) rendered correctly. The new sanitizer regressed that.

@nepenth

nepenth commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for catching this. Agreed: the sanitizer should preserve Markdown table structure while still dropping unsafe attributes.

Opened a focused follow-up fix here: #44848

The fix adds table, thead, tbody, tfoot, tr, th, and td to the Matrix sanitizer allowlist, with regression coverage that table HTML is preserved and unsafe attributes are still stripped.

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 P1 High — major feature broken, no workaround platform/matrix Matrix adapter (E2EE) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants