Skip to content

fix(slack): preserve thread context for Agents & Assistants DM root messages#63840

Merged
steipete merged 4 commits into
openclaw:mainfrom
zozo123:islo/build-openclaw-63659-claude-1775744458
May 30, 2026
Merged

fix(slack): preserve thread context for Agents & Assistants DM root messages#63840
steipete merged 4 commits into
openclaw:mainfrom
zozo123:islo/build-openclaw-63659-claude-1775744458

Conversation

@zozo123

@zozo123 zozo123 commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Preserves thread context for Agents & Assistants DM root messages in Slack integration.

Closes #63659

Testing

  • Relevant tests pass

This PR was developed with AI assistance (Claude). All code has been reviewed and tested.
Built with islo.dev

Real behavior proof

  • Behavior or issue addressed: Slack Agents & Assistants DM root messages with thread_ts == ts now preserve MessageThreadId when the message pipeline has normalized the event as a direct message, including cases where Slack omits or misreports raw channel_type. Non-DM self-thread roots remain top-level.
  • Real environment tested: Local OpenClaw checkout on macOS from PR head 37de7dc8d6, Node 25.2.1, pnpm 10.33.2, running the production Slack routing resolver from this branch.
  • Exact steps or command run after this patch: Ran pnpm exec tsx -e '<script invoking resolveSlackRoutingContext for D-prefix DM roots and a channel self-thread root>' in the PR worktree.
  • Evidence after fix: Console output from the local OpenClaw routing smoke:
{"case":"dm-root-wrong-channel-type","sessionKey":"agent:main:slack:direct:u3","messageThreadId":"1770408530.000000","replyToId":"1770408530.000000"}
{"case":"dm-root-missing-channel-type","sessionKey":"agent:main:slack:direct:u3","messageThreadId":"1770408531.000000","replyToId":"1770408531.000000"}
{"case":"channel-self-thread-root","sessionKey":"agent:main:slack:channel:c123","messageThreadId":null,"replyToId":"1770408532.000000"}
  • Observed result after fix: Normalized DM root events kept the direct session and preserved the Slack root timestamp as MessageThreadId; the channel self-thread root kept channel-session routing and did not set MessageThreadId.
  • What was not tested: Live Slack workspace delivery was not tested in this environment. Focused Slack Vitest coverage and local pnpm check:changed also passed after the smoke.

@greptile-apps

greptile-apps Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a thread-context loss bug in the Slack Agents & Assistants DM flow. When a DM is opened, Slack sets thread_ts === ts on the root message; previously, messageThreadId was only populated for that case when replyToMode was "all", so subsequent tool-call replies (subagent results) fell back to the top-level channel. The fix adds a hasThreadTs guard that sets messageThreadId unconditionally when thread_ts is present, regardless of replyToMode.

Confidence Score: 5/5

Safe to merge — the fix is narrowly scoped to the thread-root case and is well-covered by the new test.

All findings are P2 (a minor redundant ternary arm). The logic is correct, the only affected code path is the !isThreadReply && hasThreadTs case (Agents & Assistants DM roots), existing tests remain green, and a new parameterized test validates the fix across all relevant modes.

No files require special attention.

Vulnerabilities

No security concerns identified. The change only affects in-process thread-ID resolution logic with no external input handling, auth, or data-persistence implications.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/slack/src/threading.ts
Line: 29-35

Comment:
**Simplifiable ternary — first arm is now redundant**

Since `isThreadReply` being `true` implies `hasThreadTs` is `true`, the `isThreadReply ? incomingThreadTs` arm is subsumed by the new `hasThreadTs ? incomingThreadTs` arm. The two-step ternary can be collapsed to one without changing behavior:

```suggestion
  const messageThreadId = hasThreadTs
    ? incomingThreadTs
    : params.replyToMode === "all"
      ? messageTs
      : undefined;
```

This is a minor clarity suggestion; semantics are identical either way.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(slack): preserve thread context for ..." | Re-trigger Greptile

Re-review progress:

Comment thread extensions/slack/src/threading.ts Outdated
Comment on lines +29 to +35
const messageThreadId = isThreadReply
? incomingThreadTs
: params.replyToMode === "all"
? messageTs
: undefined;
: hasThreadTs
? incomingThreadTs
: params.replyToMode === "all"
? messageTs
: undefined;

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.

P2 Simplifiable ternary — first arm is now redundant

Since isThreadReply being true implies hasThreadTs is true, the isThreadReply ? incomingThreadTs arm is subsumed by the new hasThreadTs ? incomingThreadTs arm. The two-step ternary can be collapsed to one without changing behavior:

Suggested change
const messageThreadId = isThreadReply
? incomingThreadTs
: params.replyToMode === "all"
? messageTs
: undefined;
: hasThreadTs
? incomingThreadTs
: params.replyToMode === "all"
? messageTs
: undefined;
const messageThreadId = hasThreadTs
? incomingThreadTs
: params.replyToMode === "all"
? messageTs
: undefined;

This is a minor clarity suggestion; semantics are identical either way.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/slack/src/threading.ts
Line: 29-35

Comment:
**Simplifiable ternary — first arm is now redundant**

Since `isThreadReply` being `true` implies `hasThreadTs` is `true`, the `isThreadReply ? incomingThreadTs` arm is subsumed by the new `hasThreadTs ? incomingThreadTs` arm. The two-step ternary can be collapsed to one without changing behavior:

```suggestion
  const messageThreadId = hasThreadTs
    ? incomingThreadTs
    : params.replyToMode === "all"
      ? messageTs
      : undefined;
```

This is a minor clarity suggestion; semantics are identical either way.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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

Copy link
Copy Markdown

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: 1bdbc97069

ℹ️ 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".

Comment thread extensions/slack/src/threading.ts Outdated
Comment on lines +31 to +33
: hasThreadTs
? incomingThreadTs
: params.replyToMode === "all"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict thread-root override to DM assistant threads

This change sets messageThreadId for every event with thread_ts, including auto-created top-level cases where thread_ts === ts and isThreadReply is false. Downstream, buildSlackThreadingToolContext treats any MessageThreadId as an explicit target and forces replyToMode to all (extensions/slack/src/threading-tool-context.ts:19-21), so channel/group conversations configured as off/first/batched can have tool/subagent outputs forced into a thread despite resolveSlackThreadTargets classifying these as non-thread replies. This should be scoped to the intended Agents/Assistants DM-root scenario rather than all thread_ts === ts messages.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added channel: slack Channel integration: slack size: XS labels Apr 9, 2026
@zozo123

zozo123 commented Apr 12, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

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: 1bdbc97069

ℹ️ 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".

Comment thread extensions/slack/src/threading.ts Outdated
Comment on lines +31 to +32
: hasThreadTs
? incomingThreadTs

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope thread-root MessageThreadId assignment to DMs only

This branch now sets messageThreadId for any event where thread_ts is present, including top-level thread_ts == ts messages that are intentionally classified as non-thread replies. In channel/group contexts with replyToMode set to off/first/batched, that changes behavior because downstream Slack tool threading treats any MessageThreadId as an explicit thread target and forces threaded follow-ups, so tool/subagent outputs can be posted in a thread even when normal reply mode says not to. This should be gated to the DM Agents/Assistants root-message case (or equivalent chat-type check) rather than all hasThreadTs events.

Useful? React with 👍 / 👎.

@zozo123 zozo123 force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from 1bdbc97 to 108b60c Compare April 12, 2026 07:48

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

Copy link
Copy Markdown

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: 108b60c1f1

ℹ️ 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".

Comment thread extensions/slack/src/threading.ts Outdated
Comment on lines +31 to +32
: hasThreadTs
? incomingThreadTs

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope thread-root MessageThreadId assignment to DMs

Setting messageThreadId whenever hasThreadTs is true makes non-reply events with thread_ts === ts look like explicit thread targets. Downstream, buildSlackThreadingToolContext treats any MessageThreadId as explicit and forces replyToMode to all, so channel/group turns configured as off/first/batched can have tool/subagent outputs unexpectedly routed into a thread. Fresh evidence: the newly added test in extensions/slack/src/threading.test.ts uses a channel id (C1) while asserting this behavior for non-all modes, confirming the regression now applies outside the DM Assistant-root case.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S and removed size: XS labels Apr 12, 2026

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

Copy link
Copy Markdown

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: 2ca2221a0a

ℹ️ 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".

Comment thread extensions/slack/src/threading.ts Outdated
// buildSlackThreadingToolContext treats any MessageThreadId as an explicit
// thread target and overrides replyToMode to "all".
const isDmAssistantThread =
hasThreadTs && !isThreadReply && params.message.channel_type === "im";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use normalized DM detection for assistant thread roots

The new isDmAssistantThread branch keys only on params.message.channel_type === "im", but this Slack path already handles events where channel_type is missing or incorrect for D-prefixed DMs (normalized in extensions/slack/src/monitor/message-handler/prepare.ts). In those DM events, root assistant messages with thread_ts === ts still produce messageThreadId: undefined for off/first/batched, so buildSlackThreadingToolContext does not force explicit threading and later tool/subagent sends can still fall back to the channel root. Please derive this condition from normalized channel type (or channel-id inference) instead of the raw event field.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Workflow note: Future ClawSweeper reviews update this same comment in place.

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.

Summary
The branch adds normalized direct-message awareness to Slack thread-context resolution, passes it from routing, adds focused Slack regression tests for DM self-thread roots and non-DM guards, and updates the changelog.

Reproducibility: yes. at source level. Current main computes normalized isDirectMessage before routing but does not pass it into resolveSlackThreadContext, so D-prefix DM roots with thread_ts equal to ts and direct reply modes off, first, or batched leave MessageThreadId undefined.

PR rating
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Summary: The patch is focused and clean with sufficient resolver-level proof, while live Slack transport proof remains an optional maintainer confidence booster.

Rank-up moves:

  • Run a Slack Desktop smoke only if maintainers require transport-level proof before landing.
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.

Real behavior proof
Sufficient (live_output): The PR body includes after-fix console output from the production Slack routing resolver for wrong/missing channel_type DM roots and the non-DM guard case; live Slack workspace delivery was not exercised.

Mantis proof suggestion
A Slack Desktop smoke would materially show whether tool or subagent replies stay in the originating assistant-DM thread in the real transport. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

slack desktop smoke: verify assistant DM root tool/subagent replies stay in the originating Slack thread and non-DM self-thread roots stay top-level.

Risk before merge

  • Live Slack workspace delivery was not exercised; the supplied proof is production resolver output plus tests and CI metadata.
  • Merging intentionally changes Slack MessageThreadId for normalized direct self-thread roots, which affects where tool and subagent messages are delivered and how assistant-DM thread state is preserved.

Maintainer options:

  1. Accept resolver proof and land (recommended)
    Maintainers can land with the current source review, focused tests, CI, and live resolver output if they do not require live Slack workspace proof.
  2. Request Slack Desktop smoke
    Before merge, ask for a real Slack workspace proof showing assistant-DM tool or subagent replies stay in the originating thread while non-DM self-thread roots stay top-level.

Next step before merge
No repair work remains; maintainers need to decide whether resolver-level proof is enough or request optional Slack Desktop proof before merge.

Security
Cleared: The diff is limited to Slack in-process routing logic, focused tests, and changelog text with no dependency, CI, secret, packaging, or downloaded-code surface.

Review details

Best possible solution:

Land this PR, or an equivalent Slack-owned patch, after maintainers accept resolver-level proof or request an optional Slack Desktop transport smoke.

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

Yes, at source level. Current main computes normalized isDirectMessage before routing but does not pass it into resolveSlackThreadContext, so D-prefix DM roots with thread_ts equal to ts and direct reply modes off, first, or batched leave MessageThreadId undefined.

Is this the best way to solve the issue?

Yes. Passing the already-normalized direct-message classification into the existing Slack thread resolver is the narrow maintainable fix, and the latest branch keeps non-DM self-thread roots unset.

Label justifications:

  • P1: The PR addresses a broken Slack agent delivery path where tool or subagent replies can lose the originating assistant-DM thread context.
  • merge-risk: 🚨 message-delivery: The diff changes Slack reply targeting data that determines where tool and subagent messages are delivered.
  • merge-risk: 🚨 session-state: The changed MessageThreadId value affects whether assistant-DM turns preserve the intended thread context across follow-up replies.
  • rating: 🐚 platinum hermit: Current PR rating is 🐚 platinum hermit because proof is 🐚 platinum hermit, patch quality is 🦞 diamond lobster, and The patch is focused and clean with sufficient resolver-level proof, while live Slack transport proof remains an optional maintainer confidence booster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix console output from the production Slack routing resolver for wrong/missing channel_type DM roots and the non-DM guard case; live Slack workspace delivery was not exercised.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix console output from the production Slack routing resolver for wrong/missing channel_type DM roots and the non-DM guard case; live Slack workspace delivery was not exercised.

What I checked:

  • Current main drops DM self-thread roots outside replyToMode=all: On current main, resolveSlackThreadContext only sets messageThreadId for true thread replies or when replyToMode is all; a direct self-thread root with thread_ts equal to ts and direct reply modes off, first, or batched remains unset. (extensions/slack/src/threading.ts:23, f39f56a096a7)
  • Normalized DM state exists before thread resolution: resolveSlackRoutingContext already receives isDirectMessage and derives chatType from it, but current main calls resolveSlackThreadContext without passing that normalized classification. (extensions/slack/src/monitor/message-handler/prepare-routing.ts:180, f39f56a096a7)
  • D-prefix normalization is the existing Slack contract: normalizeSlackChannelType treats D-prefixed channel IDs as DMs and overrides contradictory raw channel_type, which supports using normalized direct-message state instead of the raw Slack event field. (extensions/slack/src/monitor/channel-type.ts:25, f39f56a096a7)
  • Non-DM guard is necessary for delivery safety: buildSlackThreadingToolContext treats MessageThreadId, TransportThreadId, or an explicit reply thread as an explicit target and forces effective replyToMode to all, so a broad self-thread override would change channel or group delivery behavior. (extensions/slack/src/threading-tool-context.ts:20, f39f56a096a7)
  • Latest PR head implements the intended normalized-DM guard: The PR adds an optional isDirectMessage parameter to resolveSlackThreadContext, sets messageThreadId for self-thread roots only when that normalized flag is true, and passes the flag from Slack routing. (extensions/slack/src/threading.ts:12, f082ac106a02)
  • Latest PR head covers the core regression cases: The diff adds tests for raw im, wrong or missing channel_type, non-DM self-thread roots, and distinct concurrent assistant-DM root MessageThreadId values. (extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts:635, f082ac106a02)

Likely related people:

  • steipete: Authored merged Slack assistant lifecycle and assistant thread-context preservation work that changed the same routing, prepare, send, and Slack docs surfaces. (role: recent Slack assistant and thread-routing contributor; confidence: high; commits: 80eeb688c1c2, 842e6f16437c, c49d909b60a6; files: extensions/slack/src/monitor/message-handler/prepare-routing.ts, extensions/slack/src/monitor/message-handler/prepare.ts, extensions/slack/src/threading-tool-context.ts)
  • kagura-agent: Authored the merged change that routes Slack DM thread replies to the main DM session and touched the same prepare-routing and tool-threading boundary. (role: adjacent DM thread-routing contributor; confidence: medium; commits: ffdc7aa7a6d5; files: extensions/slack/src/monitor/message-handler/prepare-routing.ts, extensions/slack/src/monitor/message-handler/prepare.ts, extensions/slack/src/threading-tool-context.ts)
  • bek91: Authored recent merged Slack inbound thread session routing work in the same prepare-routing and thread session tests. (role: adjacent Slack inbound thread-session contributor; confidence: medium; commits: aac83e00cfe7; files: extensions/slack/src/monitor/message-handler/prepare-routing.ts, extensions/slack/src/monitor/message-handler/prepare.test.ts, extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts)
  • scoootscooob: Moved the Slack implementation into the current extensions/slack/src plugin boundary, including the affected threading and routing files. (role: Slack extension migration contributor; confidence: medium; commits: 8746362f5ebf; files: extensions/slack/src/threading.ts, extensions/slack/src/monitor/message-handler/prepare-routing.ts, extensions/slack/src/threading-tool-context.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against f39f56a096a7.

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

What this changes:

The PR changes Slack thread-context resolution and tests to set MessageThreadId for DM assistant thread-root messages regardless of reply mode, and also adds context_window_exceeded matching to an agents overflow-compaction test harness.

Required change before merge:

This is a concrete, narrow PR repair: the desired behavior, affected Slack files, regression boundary, and validation path are clear, and the remaining work is code/test cleanup rather than product or security judgment.

Review details

Best possible solution:

Implement the Slack fix using the normalized direct-message classification already available in the message pipeline, preserve MessageThreadId only for assistant-DM self-thread roots, keep non-DM self-thread roots unset, and cover raw im, D-prefix wrong/missing channel_type, non-DM roots, and concurrent assistant-DM roots in focused Slack tests.

Acceptance criteria:

  • pnpm test extensions/slack/src/threading.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts extensions/slack/src/threading-tool-context.test.ts
  • pnpm exec oxfmt --check --threads=1 extensions/slack/src/threading.ts extensions/slack/src/threading.test.ts extensions/slack/src/monitor/message-handler/prepare-routing.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts
  • pnpm check:changed in Testbox before handoff if code/test changes are promoted

What I checked:

Likely related people:

  • steipete: Available local blame/log for the relevant Slack threading, routing, normalized channel-type, and tool-threading files attributes the current implementation to commit a972c9e; this is a routing signal from the available checkout history, not a fault assignment. (role: recent maintainer / available-history owner; confidence: low; commits: a972c9ec4547, acae48b790fa; files: extensions/slack/src/threading.ts, extensions/slack/src/monitor/message-handler/prepare-routing.ts, extensions/slack/src/monitor/channel-type.ts)

Remaining risk / open question:

  • The PR branch's raw channel_type === "im" guard can miss D-prefixed Slack DMs when Slack omits or misreports channel_type, while current OpenClaw already normalizes those events as direct messages.
  • A fix that sets MessageThreadId for every thread_ts === ts event would change channel/group behavior because Slack tool threading forces replyToMode to all when that field is present.
  • The unrelated agents overflow-compaction harness edit expands review scope and should be split or dropped before this Slack fix lands.

Codex review notes: model gpt-5.5, reasoning high; reviewed against acae48b790fa.

@zozo123 zozo123 force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from 2ca2221 to 37de7dc Compare May 5, 2026 06:38
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed agents Agent runtime and tooling triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 5, 2026
@zozo123 zozo123 force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from 37de7dc to 4824dbd Compare May 5, 2026 06:48
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@openclaw-barnacle openclaw-barnacle Bot added the proof: supplied External PR includes structured after-fix real behavior proof. label May 12, 2026
@zozo123 zozo123 force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from 4824dbd to f1ca934 Compare May 17, 2026 20:02
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 17, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. P1 High-priority user-facing bug, regression, or broken workflow. impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. labels May 17, 2026
@zozo123 zozo123 force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from f1ca934 to f082ac1 Compare May 17, 2026 21:01
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 17, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. 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. and removed impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. labels May 17, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: 🥚 common Mossy Clawlet

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: 🥚 common.
Trait: stacks clean commits.
Image traits: location release reef; accessory lint brush; palette charcoal, cyan, and signal green; mood proud; pose stepping out of a freshly hatched shell; shell starlit enamel shell; lighting bright celebratory glints; background miniature CI buoys.
Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Mossy Clawlet in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@zozo123

zozo123 commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper hatch

@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper PR egg hatch requested.

I queued a comment sync for this PR. If the egg is hatchable, ClawSweeper will generate the image once and update the existing review comment.
Action: PR egg hatch queued (workflow sweep.yml, event repository_dispatch).
The ASCII egg stays as the fallback.

@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@zozo123

zozo123 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex rebase and update to main

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@zozo123

zozo123 commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper can you kindly rebase and update to main

zozo123 and others added 4 commits May 30, 2026 22:21
The previous fix unconditionally set messageThreadId when hasThreadTs was
true, which affected channel/group messages where thread_ts == ts (auto-
created top-level threads). This caused buildSlackThreadingToolContext to
force replyToMode to "all" in non-DM contexts, unexpectedly routing tool
and subagent outputs into threads.

Now the override only applies when channel_type is "im", correctly
limiting it to the Slack Agents & Assistants DM root-message scenario.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@steipete steipete force-pushed the islo/build-openclaw-63659-claude-1775744458 branch from f082ac1 to f8aa242 Compare May 30, 2026 21:22
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 30, 2026

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

Copy link
Copy Markdown

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: f8aa24262c

ℹ️ 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".

// where thread_ts == ts. Non-DM self-thread roots must stay unset because
// downstream tool threading treats MessageThreadId as an explicit thread
// target and overrides replyToMode to "all".
const isAssistantDmThreadRoot = hasThreadTs && !isThreadReply && params.isDirectMessage === true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Thread DM root replies with preserved context

When a normalized DM root only has thread_ts == ts (for example the new D-prefixed cases without assistant_thread metadata/cache), this branch now exposes MessageThreadId, so tool/subagent sends are forced into that Slack thread via buildSlackThreadingToolContext. The normal reply path still gets forcedReplyThreadTs only from assistantThreadContext and dispatch.ts recomputes thread targets without the normalized DM flag, so with replyToMode off/first/batched the agent’s final reply can remain unthreaded while tool output goes into the thread. Please carry the same preserved DM-root thread target into the reply delivery plan as well.

Useful? React with 👍 / 👎.

@steipete

Copy link
Copy Markdown
Contributor

Landing proof for the rebased head f8aa24262c0c1a363b5f9dc43e174d7b89f513ea.

  • Local review: no blocking findings; checked Slack routing/threading, prepare, dispatch, and tool-thread context paths.
  • Local commands:
    • git rebase origin/main
    • git diff --check origin/main...HEAD
    • node scripts/run-vitest.mjs extensions/slack/src/threading.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts
  • Local result: 3 test files passed, 110 tests passed.
  • CI proof:
    • CI run 26695270036: passed.
    • CodeQL Critical Quality run 26695270033: passed.
    • Dependency Guard run 26695269091: passed.
  • Maintainer fixup: rebased on current main and removed the contributor changelog edit; release-note context remains in the PR/squash path.
  • Not tested: live Slack workspace delivery.

Thanks @zozo123.

@steipete steipete 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.

Reviewed the rebased Slack routing fix and focused regression coverage. Local focused tests and current CI are green. Approved for landing.

@steipete steipete merged commit 4432554 into openclaw:main May 30, 2026
141 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via squash merge.

  • Head before merge: f8aa24262c0c1a363b5f9dc43e174d7b89f513ea
  • Merge commit: 443255461c00cad1bb9928afe06f84af3c0bc73f
  • Local proof: node scripts/run-vitest.mjs extensions/slack/src/threading.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts passed with 3 files and 110 tests.
  • CI proof: CI 26695270036, CodeQL Critical Quality 26695270033, and Dependency Guard 26695269091 passed.

Thanks @zozo123.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 31, 2026
Preserve Slack Agents & Assistants DM root thread context for tool and subagent replies even when Slack omits or misreports `channel_type`, while leaving non-DM self-thread roots top-level.

Fixes openclaw#63659.

Thanks @zozo123.
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
Preserve Slack Agents & Assistants DM root thread context for tool and subagent replies even when Slack omits or misreports `channel_type`, while leaving non-DM self-thread roots top-level.

Fixes openclaw#63659.

Thanks @zozo123.
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Preserve Slack Agents & Assistants DM root thread context for tool and subagent replies even when Slack omits or misreports `channel_type`, while leaving non-DM self-thread roots top-level.

Fixes openclaw#63659.

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

Labels

channel: slack Channel integration: slack 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. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Slack] Subagent results lose thread_ts in DM assistant threads — cross-thread contamination with concurrent requests

3 participants