Skip to content

fix(mattermost): key resolved group targets under group: namespace (#95646)#95669

Open
hansraj316 wants to merge 1 commit into
openclaw:mainfrom
hansraj316:fix/mattermost-infer-chat-type-95646
Open

fix(mattermost): key resolved group targets under group: namespace (#95646)#95669
hansraj316 wants to merge 1 commit into
openclaw:mainfrom
hansraj316:fix/mattermost-infer-chat-type-95646

Conversation

@hansraj316

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes #95646. The chat_type reported for a Mattermost private channel was non-deterministic across code paths: group when derived from the channel type (authoritative inbound path, mapMattermostChannelKind: P/G → group), but channel when derived from the delivery target string (a private channel is addressed as channel:<id>, same prefix as a public channel). The same conversation was keyed under two session namespaces — group:<id>:thread:<root> (inbound) and a phantom channel:<id>:thread:<root> (delivery) — so threaded/scheduled deliveries failed to match.

Why This Change Was Made

The outbound session route flattened every non-user target to channel, discarding an authoritative group kind that the directory resolver already produces (directory.ts emits kind: "group" for private channels / group DMs). The route now honors that resolved group kind for the session namespace and from, aligning delivery with the inbound classification. The delivery target string stays channel:<id> because Mattermost addresses all channels by id; only the session namespace is type-aware.

User Impact

Directory-resolved private-channel / group-DM deliveries now key under the same group: session as inbound, fixing the split-session / fail-closed delivery. A bare channel:<id> target with no resolved kind still resolves to channel, since the real channel type is not knowable from the target string alone — that residual path warrants live-Mattermost follow-up and is called out here rather than silently half-fixed.

Evidence

  • extensions/mattermost/src/session-route.ts: honor resolved group kind in peer.kind / chatType / from.
  • New test in extensions/mattermost/src/session-route.test.ts asserts a resolved group target keys under :group:<id> (not :channel:<id>) while keeping to: channel:<id>.
  • pnpm test extensions/mattermost/src/session-route.test.ts ✅ (7 passed).

…penclaw#95646)

A private channel / group DM is classified group by the inbound path
(mapMattermostChannelKind: P/G -> group) but the outbound session route
flattened every non-user target to channel, splitting the same
conversation across group:<id> and a phantom channel:<id> session key.
Honor an authoritative resolved group kind so delivery keys match inbound.

A bare channel:<id> target with no resolved kind still resolves to
channel since the real channel type is not knowable from the target
string alone; full coverage of that path needs live Mattermost repro.
@openclaw-barnacle openclaw-barnacle Bot added channel: mattermost Channel integration: mattermost size: XS labels Jun 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cab9efc6bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// would split the same conversation across two session namespaces (#95646).
// A bare `channel:<id>` with no resolved kind stays `channel` since the real
// channel type is not knowable from the target string alone.
const isGroup = !isUser && resolvedKind === "group";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Keep public Mattermost channels out of group sessions

When a target is a public Mattermost channel resolved through directory or normalization, resolvedTarget.kind can still be group: extensions/mattermost/src/mattermost/directory.ts emits kind: "group" for both O and P channels, and src/infra/outbound/target-resolver.ts classifies channel: inputs as group-like. This line now converts those public-channel sends to mattermost:group:<id>, while inbound mapMattermostChannelKind maps Mattermost O channels to channel, so outbound mirrors/thread recovery for public channels split away from the existing inbound session. Only switch to group when the resolver has proven a private/group channel, or keep ambiguous channel: targets under channel.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 29, 2026, 4:21 AM ET / 08:21 UTC.

Summary
The PR changes Mattermost outbound session routing so resolved group targets use the group session namespace while keeping the Mattermost delivery target as channel:<id>.

PR surface: Source +9, Tests +21. Total +30 across 2 files.

Reproducibility: yes. Source inspection shows current main can produce resolvedTarget.kind: "group" for public Mattermost directory targets while inbound O channels map to channel; the PR then keys those public sends as group.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: extensions/mattermost/src/session-route.test.ts, serialized state: extensions/mattermost/src/session-route.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Repair the route so group is used only after authoritative Mattermost channel type proves P or G while public O remains channel.
  • [P1] Add redacted real Mattermost proof showing private-channel delivery uses the group session namespace and public-channel delivery remains channel; updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can request @clawsweeper re-review.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body reports a focused unit test but no redacted real Mattermost output, logs, terminal output, screenshot, recording, or linked artifact showing the after-fix route behavior; private endpoints, tokens, phone numbers, and non-public channel details should be redacted. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging this PR can split public Mattermost channel sessions because directory-resolved public channels can carry kind: "group" while inbound O channels remain channel.
  • [P1] The PR body provides only a focused unit test; it does not include redacted after-fix Mattermost output, logs, screenshot, recording, or linked artifact showing real route behavior.

Maintainer options:

  1. Repair classification before merge (recommended)
    Use authoritative Mattermost channel type or equivalent resolved metadata before switching a route to group, then prove public and private channel session keys separately.
  2. Hold for a broader candidate
    Pause this narrow branch while maintainers evaluate the open broader Mattermost routing candidate, without treating that proof-blocked sibling as superseding yet.

Next step before merge

  • [P1] Human review is needed because the PR has a code-path blocker and missing contributor real behavior proof that automation cannot supply for the contributor's setup.

Security
Cleared: The diff stays within Mattermost TypeScript routing/tests and does not change dependencies, workflows, package metadata, permissions, or secret handling.

Review findings

  • [P1] Keep public Mattermost channels out of group sessions — extensions/mattermost/src/session-route.ts:30
Review details

Best possible solution:

Use Mattermost-owned authoritative channel type or equivalent resolved metadata so only proven P/G channels become group, public O channels remain channel, and the fix has focused tests plus redacted real behavior proof.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main can produce resolvedTarget.kind: "group" for public Mattermost directory targets while inbound O channels map to channel; the PR then keys those public sends as group.

Is this the best way to solve the issue?

No. Preserving resolved group is only safe after the resolver has proved Mattermost channel type P or G; as submitted, the route trusts ambiguous metadata.

Full review comments:

  • [P1] Keep public Mattermost channels out of group sessions — extensions/mattermost/src/session-route.ts:30
    resolvedTarget.kind === "group" is not an authoritative private-channel signal today: Mattermost directory results include both public O and private P channels as group directory entries. This change can key a public O channel send as mattermost:group:<id> while inbound still maps O to channel, splitting thread/session recovery for public channels.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.94

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 293f0369d03b.

Label changes

Label justifications:

  • P2: This is a bounded Mattermost session-routing bugfix with channel-specific blast radius but real session and delivery impact.
  • merge-risk: 🚨 session-state: The PR changes Mattermost session namespace selection and can mis-key conversations if public channels are classified as group.
  • merge-risk: 🚨 message-delivery: The changed route is used for outbound/threaded delivery, where a wrong namespace can suppress or misroute Mattermost replies.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body reports a focused unit test but no redacted real Mattermost output, logs, terminal output, screenshot, recording, or linked artifact showing the after-fix route behavior; private endpoints, tokens, phone numbers, and non-public channel details should be redacted. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +9, Tests +21. Total +30 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 12 3 +9
Tests 1 21 0 +21
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 33 3 +30

What I checked:

  • PR route change: At PR head, isGroup becomes true for any resolvedTarget.kind === "group", and that kind is used for peer kind, chat type, and from. (extensions/mattermost/src/session-route.ts:30, cab9efc6bbdf)
  • Ambiguous Mattermost directory metadata: Current main lists both Mattermost public O and private P channels in the group directory and emits kind: "group", so resolved group metadata is not by itself proof of a private channel. (extensions/mattermost/src/mattermost/directory.ts:85, 293f0369d03b)
  • Inbound authoritative mapping: Current main maps Mattermost G and P channel types to group, while public O falls through to channel; outbound public-channel routes must preserve that distinction. (extensions/mattermost/src/mattermost/monitor-gating.ts:12, 293f0369d03b)
  • Shared target detection is also group-like for channel-shaped input: Current main treats #... and channel: inputs as group targets before Mattermost has proved whether the target is public or private. (src/infra/outbound/target-resolver.ts:204, 293f0369d03b)
  • Mattermost upstream channel type contract: Upstream Mattermost defines O as open, P as private, D as direct, and G as group, matching the local distinction the route must preserve.
  • Related canonical issue and sibling PR: The PR closes the open canonical Mattermost private-channel chat_type issue; the open sibling candidate has broader target-resolution coverage but still has proof blockers, so it is not a safe superseding target.

Likely related people:

  • Gustavo Madeira Santana: Commit d6c13d9 moved Mattermost outbound session routing behind the plugin boundary and added the helper this PR changes. (role: introduced behavior boundary; confidence: medium; commits: d6c13d9dc01e; files: extensions/mattermost/src/session-route.ts, extensions/mattermost/src/channel.ts)
  • Vincent Koc: Commit 6eb04c8 isolated id-like target resolution in the shared outbound resolver used before the Mattermost route helper receives resolved targets. (role: adjacent target-resolution contributor; confidence: medium; commits: 6eb04c8aabdb; files: src/infra/outbound/target-resolver.ts)
  • Peter Steinberger: Commit 355b4c6 landed the earlier Mattermost private-channel-as-group mapping that defines the inbound side of this mismatch. (role: adjacent chat-type routing contributor; confidence: medium; commits: 355b4c62bcc2; files: extensions/mattermost/src/mattermost/monitor-gating.ts, extensions/mattermost/src/mattermost/monitor.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 22, 2026
@clawsweeper

clawsweeper Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

@hansraj316 thanks for the PR. ClawSweeper is still waiting on real behavior proof before this can move forward.

Useful proof can be a screenshot, short video, terminal output, copied live output, linked artifact, or redacted logs that show the changed behavior after the fix. Please redact private tokens, phone numbers, private endpoints, customer data, and anything else sensitive.

Once proof is added to the PR body or a comment, ClawSweeper or a maintainer can re-check it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: mattermost Channel integration: mattermost merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mattermost: chat_type is non-deterministic for private channels (channel-type vs target-string inference); inferTargetChatType hook not implemented

1 participant