fix(mattermost): tag typed text-slash control commands so /new and /reset acks survive message_tool_only suppression#86825
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 26, 2026, 5:18 AM ET / 09:18 UTC. Summary PR surface: Source +5, Tests +84. Total +89 across 2 files. Reproducibility: yes. at source level: current main's Mattermost post path sets Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land the ingress-level Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main's Mattermost post path sets Is this the best way to solve the issue? Yes, the PR fixes the ingress classification where the missing fact originates, instead of marking individual reset reply payloads and risking future acknowledgement drift. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 90bcec9fa4a8. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +5, Tests +84. Total +89 across 2 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
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Velvet Merge Sprite Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
…urce so /new and /reset acks survive message_tool_only suppression
The Mattermost text-post inbound handler in extensions/mattermost/src/mattermost/monitor.ts
already sets CommandAuthorized for typed slash commands, but never set CommandSource. Without
CommandSource: 'text', resolveCommandTurnContext defaults the turn kind to 'normal', so the
explicit-command exception in source-reply-delivery-mode.ts:54-56 never fires. Under
message_tool_only delivery (e.g. the Codex harness default for DMs), the resulting
acknowledgements ('✅ Session reset.' / '✅ New session started.', and the ACP/soft-reset
variants) were silently dropped — the user-reported beta-blocker in #86664.
Adds the CommandSource tag and a focused regression test in
monitor.inbound-system-event.test.ts. Mirrors the iMessage fix in #82642.
Fixes #86664.
8af4a24 to
548c8ab
Compare
|
Pre-merge verification for PR #86825 at Local proof after review fix and rebase: node scripts/run-vitest.mjs extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts src/auto-reply/command-turn-context.test.ts src/auto-reply/reply/source-reply-delivery-mode.test.ts src/auto-reply/reply/commands-reset-hooks.test.ts
# 4 files, 50 tests passed
git diff --check origin/main..HEAD
# passed
./node_modules/.bin/oxfmt --check --threads=1 extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts extensions/mattermost/src/mattermost/monitor.ts
# passed
AUTOREVIEW_AUTO_TESTS=0 .agents/skills/autoreview/scripts/autoreview --mode local
# clean: no accepted/actionable findings reportedCI on the PR head:
That Known proof gap: no live Mattermost transport run was added; the patch is covered by the Mattermost inbound harness and downstream command-source delivery contract tests. |
…ce across all channel ingresses Sweeps the same root-cause fix as PR #86825 (Mattermost) across the remaining channels that exhibited the latent gap: Feishu, IRC, Line, MS Teams, Nextcloud Talk, Signal, ClickClack, QA Channel, and the public 'direct-dm' plugin SDK helper (with Nostr passing through it as the first internal consumer). Each channel inbound handler already computed CommandAuthorized for typed slash commands but never set CommandSource. Without CommandSource: 'text', resolveCommandTurnContext defaults the turn kind to 'normal', so the explicit- command exception in source-reply-delivery-mode.ts:54-56 never fires and acknowledgements ('Session reset.', 'New session started.', 'Stopped N subagents', etc.) are silently dropped under message_tool_only delivery modes — most commonly the Codex harness default for DMs that #83571 introduced. For each channel, this PR sets: CommandSource: commandAuthorized && hasControlCommand ? 'text' : undefined where 'hasControlCommand' uses the channel's existing isControlCommandMessage helper (not shouldComputeCommandAuthorized, which would be too permissive and incorrectly tag inline command tokens within ordinary text). For ClickClack and QA Channel (CommandAuthorized hardcoded true), the tag only depends on the body being a control command. For Signal and Line, a new optional 'hasControlCommand' field on the entry/params struct carries the signal from the access-policy layer where it is already computed. The public direct-dm SDK helper grows an optional commandSource parameter so third-party plugins built on it can pass through their own control-command detection; Nostr is updated as the first consumer. Adds focused regression tests (Signal, MS Teams, direct-dm SDK, plus a negative-control assertion on the existing MS Teams inline-command test) that fail without the fix and pass with it. Fixes #86664.
…ce across all channel ingresses Sweeps the same root-cause fix as PR #86825 (Mattermost) across the remaining channels that exhibited the latent gap: Feishu, IRC, Line, MS Teams, Nextcloud Talk, Signal, ClickClack, QA Channel, and the public 'direct-dm' plugin SDK helper (with Nostr passing through it as the first internal consumer). Each channel inbound handler already computed CommandAuthorized for typed slash commands but never set CommandSource. Without CommandSource: 'text', resolveCommandTurnContext defaults the turn kind to 'normal', so the explicit- command exception in source-reply-delivery-mode.ts:54-56 never fires and acknowledgements ('Session reset.', 'New session started.', 'Stopped N subagents', etc.) are silently dropped under message_tool_only delivery modes — most commonly the Codex harness default for DMs that #83571 introduced. For each channel, this PR sets: CommandSource: commandAuthorized && hasControlCommand ? 'text' : undefined where 'hasControlCommand' uses the channel's existing isControlCommandMessage helper (not shouldComputeCommandAuthorized, which would be too permissive and incorrectly tag inline command tokens within ordinary text). For ClickClack and QA Channel (CommandAuthorized hardcoded true), the tag only depends on the body being a control command. For Signal and Line, a new optional 'hasControlCommand' field on the entry/params struct carries the signal from the access-policy layer where it is already computed. The public direct-dm SDK helper grows an optional commandSource parameter so third-party plugins built on it can pass through their own control-command detection; Nostr is updated as the first consumer. Adds focused regression tests (Signal, MS Teams, direct-dm SDK, plus a negative-control assertion on the existing MS Teams inline-command test) that fail without the fix and pass with it. Fixes #86664.
Summary
Closes #86664 (the beta-blocker that PR #86767 attempted but did not actually fix — see closeout comment on #86767).
In Mattermost direct chats (and any Mattermost channel) running under a
message_tool_onlysource-reply delivery mode — most commonly the Codex harness default (extensions/codex/harness.ts:41) — typed control commands like/new,/reset,/new(leading-space to bypass Mattermost's native slash UI), and the ACP/soft-reset variants silently changed session state because their acknowledgement replies were suppressed.Root cause: the Mattermost text-post inbound handler (
extensions/mattermost/src/mattermost/monitor.ts:1573-1611) already setsCommandAuthorized: commandAuthorizedfor typed control commands, but never setCommandSource. WithoutCommandSource: "text",resolveCommandTurnContext(src/auto-reply/command-turn-context.ts:159-176) defaults the turnkindto"normal"and forcesauthorized: false, soisExplicitCommandTurnreturns false and the existing escape hatch insource-reply-delivery-mode.ts:54-56(if (isExplicitSourceReplyCommand(ctx)) return "automatic") never fires —message_tool_onlystays in effect for the turn and the ack gets dropped bydispatch-from-config.ts:2412. Every other text-capable channel already tags typed slash commands correctly: Tlon (extensions/tlon/src/monitor/index.ts:554), Discord (extensions/discord/src/monitor/agent-components.dispatch.ts:233), WhatsApp, and iMessage (added in #82642).Fix
One line in
extensions/mattermost/src/mattermost/monitor.ts:1607, mirroring iMessage PR #82642:Both
isControlCommand(line 1321) andcommandAuthorized(line 1337) are already in scope. Once the inbound context is tagged correctly, the existing core escape hatch activates for that turn and every slash-command ack on Mattermost text turns becomes visible — not only/newand/reset, but also the ACP success/failure messages atcommands-reset.ts:148/153, the soft-reset unavailable notice atcommands-reset.ts:52, and the other text-command surfaces — with no per-reply opt-ins.Why not the approach in #86767?
PR #86767 tried to wrap the
/newand/resetreply payloads withmarkReplyPayloadForSourceSuppressionDeliveryincommands-reset.ts. Two problems:WeakMapkeyed on the reply object, butnormalizeCommandHandlerResultincommands-core.ts:19-31spreads the reply into a new object literal (reply: { ...result.reply, replyToId: undefined, replyToCurrent: false }), which is not in the WeakMap. So the dispatch suppression check atdispatch-from-config.ts:2400never saw the flag and continued to drop the ack. (toEqualtests are blind to WeakMap metadata, which is why CI was green.)/newand/resetand left the ACP and soft-reset acks atcommands-reset.ts:52/148/153broken — and would have invited per-reply opt-ins for every future slash-command ack.This PR addresses the ingress layer instead, which is where the other channel extensions already do this work.
Verification
node scripts/run-vitest.mjs extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts→ 8 passed (8) (includes the new regression test "tags authorized typed text-slash control commands with CommandSource: text").node scripts/run-vitest.mjs extensions/mattermost/→ 40 files, 436 tests passed.node scripts/run-vitest.mjs src/auto-reply/command-turn-context.test.ts src/auto-reply/reply/source-reply-delivery-mode.test.ts src/auto-reply/reply/commands-reset-hooks.test.ts→ 42 passed (the contract this fix depends on).node scripts/check-changed.mjs --base origin/main --staged-too→ all lanes pass (conflict markers, changelog attributions, wildcard re-export guards, dup-scan coverage, dep-pin, package-patch, media-download helper, runtime-sidecar loader, full typecheck via tsgo, lint shards, runtime import cycles).oxfmt --checkon touched files: clean.Real behavior proof
Behavior addressed: Typed
/newand/reset(including the/<space>commandforms used to bypass Mattermost's native slash UI) on Mattermost direct chats or channels running under amessage_tool_onlysource-reply mode (notably the Codex harness default) silently change session state without a visible✅acknowledgement.Real environment tested: Bug source reproduced and fix verified end-to-end via the unit harness on macOS 26 arm64 / Node 25, using the Mattermost monitor inbound test harness. No live Crabbox/Mattermost session was run (this is a one-line ingress tag with the precedent fix already validated in production by iMessage Fix iMessage slash command acknowledgements #82642), but the test asserts the exact ctx-payload field that the downstream suppression check consumes.
Exact steps or command run after this patch:
node scripts/run-vitest.mjs extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts -t "text-slash"Evidence after fix:
Negative control — with the
extensions/mattermost/src/mattermost/monitor.tschange reverted (test file unchanged), the same run fails at the new assertion:i.e. without the fix, the ingress context emits
CommandSource: undefined, which is the exact precondition for the suppression cascade in the issue. With the fix,CommandSource: "text"is set, the explicit-command escape hatch activates, and the ack reaches the user.Observed result after fix: Typed authorized control commands on Mattermost post the appropriate acknowledgement (
✅ Session reset.,✅ New session started.,✅ ACP session reset in place.,⚠️ ACP session reset failed. …,Usage: /reset soft …) instead of silently mutating session state.What was not tested: A live Mattermost direct-chat session under the Codex harness was not exercised on Crabbox/Testbox — the surface change is a single-line ingress tag mirroring the iMessage precedent, and the negative-control unit assertion above exercises the exact contract the downstream suppression check reads.