fix(mattermost): implement inferTargetChatType hook to resolve private channel chat_type#95655
fix(mattermost): implement inferTargetChatType hook to resolve private channel chat_type#95655lsr911 wants to merge 7 commits into
Conversation
…el chat_type resolution Mattermost private channels (type P) are addressed as channel:<id> but authoritatively typed as group. The lossy target-string prefix inference returns 'channel' before the plugin hook can override it, causing non-deterministic chat_type across inbound (type-based) and delivery (prefix-based) code paths. Changes: - src/infra/outbound/targets.ts: reorder inferChatTypeFromTarget to consult the plugin hook BEFORE the channel:/thread: prefix default, so plugins can override the chat type for ambiguous prefixes. - extensions/mattermost/src/channel.ts: add inferTargetChatType hook that resolves user:→direct, group:→group, and channel:<id> via a module-level cache populated by the monitor. - extensions/mattermost/src/session-route.ts: propagate 'group' chatType from resolvedTarget.kind through session routing. - extensions/mattermost/src/mattermost/monitor-gating.ts: add module-level mattermostChannelKindCache + setter. - extensions/mattermost/src/mattermost/monitor-resources.ts: populate the cache when channel info is fetched. Closes openclaw#95646
|
Codex review: needs real behavior proof before merge. Reviewed June 24, 2026, 12:10 AM ET / 04:10 UTC. Summary PR surface: Source +114. Total +114 across 5 files. Reproducibility: yes. for source-level reproduction: current code maps Mattermost private channel type Review metrics: none identified. Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Make the authoritative Mattermost channel-kind source account-consistent across hook and route reads, cover private/public/cold-cache behavior, and add redacted live Mattermost proof before merge. Do we have a high-confidence way to reproduce the issue? Yes for source-level reproduction: current code maps Mattermost private channel type Is this the best way to solve the issue? No as submitted. The plugin boundary and session-route repair are the right layer, but the cache scope mismatch means the new hook path remains ineffective for monitor-populated account-scoped entries. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against cf86a9799c3f. Label changesLabel justifications:
Evidence reviewedPR surface: Source +114. Total +114 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
- Remove non-existent 'openclaw/plugin-sdk/chat-type' import - Remove unused 'mapMattermostChannelTypeToChatType' import - Remove unused 'setMattermostChannelKindCache' from monitor.ts
Per ClawSweeper review: resolvedTarget.kind === 'group' is not authoritative when the cache is cold — core target resolution can classify channel: as group, moving public channel deliveries into the wrong session namespace. Revert the isGroup check so session-route chatType stays 'channel' for non-user targets. The inferTargetChatType hook + targets.ts reorder still correctly resolve chatType for delivery (resolveHeartbeatDeliveryChatType path), while the session route preserves the existing channel namespace for cold-cache public channels.
Per ClawSweeper review: the heartbeat delivery path (resolveHeartbeatDeliveryTargetWithSessionRoute) calls the session-route resolver which unconditionally returned 'channel' for non-user targets, undoing the inferTargetChatType hook's correct 'group' for private channels. Use the monitor-populated mattermostChannelKindCache in the session route: - Cache hit 'group' → chatType: 'group' (authoritative private channel) - Cache hit 'channel' → chatType: 'channel' (public channel) - Cold miss → chatType: 'channel' (safe default, no regression) The cache is populated by the Mattermost monitor when channel info is fetched from the API, so entries carry authoritative channel type.
…or private channels - Align peer.kind, from, and to in session-route with the resolved chatType so private-channel group routes share the same session-key namespace instead of using the channel: prefix for group-typed channels. - Add TTL expiry (5 min) to mattermostChannelKindCache so stale entries cannot persist beyond the source channelCache lifecycle. - Include accountId in cache keys when available to prevent cross-account cache pollution. Fixes: peer.kind mismatch flagged by ClawSweeper (session-route.ts:52) Fixes: unscoped/immortal cache flagged by ClawSweeper (monitor-gating.ts:8) Ref: openclaw#95646
49cf24c to
82e8943
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Fixes oxlint error in monitor-gating.ts line 31.
Fixes TS2345: params.accountId is string | null | undefined but getMattermostChannelKind expects string | undefined.
|
Gentle ping — this PR has been waiting for review for 5 days. The ClawSweeper review requested real behavior proof; happy to add it when guidance is available. |
|
Closing after 5+ weeks with no maintainer review activity. ClawSweeper rated unranked krab (needs proof). The Mattermost private channel fix in this branch can be resubmitted when maintainer guidance is available. |
What Problem This Solves
Mattermost private channels (type P) are authoritatively resolved to chat_type
group, but the session route was still building session keys under thechannel:namespace. This caused private-channel deliveries to be mirrored undermattermost:channel:<id>instead of sharing thegroupsession namespace, leading to non-deterministic session routing and potential delivery misclassification.Additionally, the channel-kind cache was module-global with no TTL, meaning stale or cross-account entries could persist indefinitely and misclassify subsequent channel lookups.
Why This Change Was Made
Two defects identified by automated review:
session-route.ts:52 —
peer.kind,from, andtowere always set tochannelfor non-user targets, even when the resolvedchatTypewasgroup. The SDK route helper builds the base session key frompeer, so private channels ended up in the wrong session namespace.monitor-gating.ts:8 —
mattermostChannelKindCachewas aMap<string, ChatType>with no expiration and no account scoping, while the sourcechannelCachein monitor-resources.ts is per-resource and TTL-bound. This created a lifecycle mismatch where stale caches could outlive their source.Changes
extensions/mattermost/src/session-route.ts— DerivepeerKindfrom the cached channel kind, and use it forpeer.kind,from, andtoso group-typed private channels use thegroup:/mattermost:group:namespace.extensions/mattermost/src/mattermost/monitor-gating.ts— Replace the bareMap<string, ChatType>with TTL-expiring entries (5 min, matching the source channelCache TTL) and account-scoped keys. Export a backward-compatiblemattermostChannelKindCachefacade so existing callers continue to work.extensions/mattermost/src/mattermost/monitor-resources.ts— PassaccountIdtosetMattermostChannelKindCachefor account-scoped cache entries.Evidence
Test command
Test output
Behavior verification
Related
Closes #95646