Skip to content

fix(slack): prefer current thread session for inherited outbound replies#97168

Merged
steipete merged 3 commits into
openclaw:mainfrom
LiuwqGit:feat/issue-96535-slack-outbound-reply-delivery-mirror
Jun 30, 2026
Merged

fix(slack): prefer current thread session for inherited outbound replies#97168
steipete merged 3 commits into
openclaw:mainfrom
LiuwqGit:feat/issue-96535-slack-outbound-reply-delivery-mirror

Conversation

@LiuwqGit

@LiuwqGit LiuwqGit commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Closes #96535

What Problem This Solves

Slack message-tool replies inside an existing thread could mirror each inherited child reply timestamp into a new :thread:<childTs> session instead of reusing the conversation's root thread session. Active Slack conversations accumulated near-empty delivery-mirror sessions and fragmented persisted history.

Why This Change Was Made

Slack already receives the canonical root threadId, the child replyToId, and the tri-state replyToIsExplicit signal in its resolveReplyTransport hook. The fix now uses that existing Slack-owned seam:

  • inherited reply (replyToIsExplicit === false): prefer the valid root thread timestamp;
  • explicit reply (true): preserve the requested child reply target;
  • unknown/omitted signal: preserve the shipped reply-first behavior;
  • reply mode off: preserve the existing reply suppression behavior.

Canonicalizing before outbound delivery keeps Slack's actual thread_ts and the delivery-mirror session route aligned. No public channel/session-route API was added.

User Impact

Threaded Slack replies now stay attached to the conversation's existing root session instead of creating one mirror-only session per child reply. Explicit reply targeting and existing callers that omit the new-origin signal keep their prior behavior.

Evidence

  • Source repro: current main passed inherited child replyToId before the root threadId in Slack's reply transport, then mirrored the resulting child timestamp through the default reply-first session route.
  • Focused tests after repair and after rebase:
    • node scripts/run-vitest.mjs extensions/slack/src/channel.test.ts src/infra/outbound/message-action-runner.threading.test.ts extensions/slack/src/thread-ts.test.ts
    • 2 Vitest shards, 72 tests passed.
  • Blacksmith Testbox through Crabbox:
  • Fresh branch autoreview: no accepted/actionable findings; patch correct at 0.94 confidence.
  • git diff --check passed.

Known proof gap: no credentialed live Slack workspace replay was run. The issue includes deployment evidence, and the repaired source path plus focused tri-state regression tests prove the routing decision directly.

@openclaw-barnacle openclaw-barnacle Bot added channel: slack Channel integration: slack size: S labels Jun 27, 2026
@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 7:30 PM ET / 23:30 UTC.

Summary
The PR changes Slack reply transport resolution so known inherited child replies prefer the root thread timestamp while preserving explicit and unknown reply targeting, with focused resolver tests.

PR surface: Source +3, Tests +27. Total +30 across 2 files.

Reproducibility: yes. at source level: inherited reply metadata can become replyTo, Slack auto-thread recovery can skip when that value is a valid Slack timestamp, and route selection can still use that child timestamp first. I did not run a credentialed Slack workspace replay.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #96535
Summary: This PR is a candidate fix for the canonical Slack outbound delivery-mirror session-sprawl issue, with one overlapping open candidate still unmerged.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

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

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

Rank-up moves:

  • [P1] Add route-level regression coverage that exercises inherited Slack child replyToId, root/current thread recovery, and outbound mirror session-key selection.
  • [P1] Add redacted live Slack workspace or session-store proof showing outbound replies append to the existing thread session after the fix.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR includes focused tests, Testbox/CI, diff-check, and source-review evidence, but no redacted real Slack workspace or session-store after-fix output. 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.

Mantis proof suggestion
A real Slack/session-store smoke would materially prove the transport and mirror-session behavior that unit tests cannot show. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis slack desktop smoke: verify threaded Slack outbound replies mirror into the existing thread session and do not create sibling delivery-mirror sessions.

Risk before merge

  • [P1] Merging as written can leave the Slack outbound mirror session-sprawl bug unresolved because inherited Slack child timestamps can bypass resolveReplyTransport and still win route selection.
  • [P1] The PR has tests and CI/Testbox evidence, but no redacted live Slack workspace or session-store after-fix proof showing mirrors append to the existing thread session.
  • [P1] A second open candidate, fix(slack): keep outbound replies in thread sessions #96650, targets the same canonical issue, so maintainers still need one landing path.

Maintainer options:

  1. Repair Route-Level Canonicalization (recommended)
    Update Slack outbound route selection or auto-thread recovery so an inherited child reply with a current/root thread session cannot become the mirror session key.
  2. Choose One Canonical Candidate
    Compare this PR with fix(slack): keep outbound replies in thread sessions #96650 and keep one implementation path for the linked Slack delivery-mirror issue.
  3. Pause For Real Transport Proof
    If maintainers cannot obtain redacted Slack or session-store proof, hold the PR rather than merging a session-state fix whose user path is still unproven.

Next step before merge

  • [P1] Human follow-up is needed to choose the canonical candidate, repair the route-level gap, and obtain real Slack or session-store proof that automation cannot supply for the contributor.

Security
Cleared: No concrete security or supply-chain concern was found; the diff is limited to Slack TypeScript routing logic and tests with no dependency, workflow, lockfile, script, or secret-surface changes.

Review findings

  • [P1] Canonicalize the mirror route before reply transport can be skipped — extensions/slack/src/channel.ts:824-825
Review details

Best possible solution:

Land one canonical Slack route-level fix that makes inherited same-thread outbound mirrors use the root/current thread session, preserves explicit and unknown reply targeting, and is backed by real Slack or session-store proof.

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

Yes, at source level: inherited reply metadata can become replyTo, Slack auto-thread recovery can skip when that value is a valid Slack timestamp, and route selection can still use that child timestamp first. I did not run a credentialed Slack workspace replay.

Is this the best way to solve the issue?

No as written. The current branch changes only Slack resolveReplyTransport, but the reported mirror-session route can bypass that hook when no root threadId is resolved.

Full review comments:

  • [P1] Canonicalize the mirror route before reply transport can be skipped — extensions/slack/src/channel.ts:824-825
    This swaps replyToId and threadId only inside Slack resolveReplyTransport, but resolveAndApplyOutboundThreadId calls that hook only after a root resolvedThreadId exists. When an inherited Slack child timestamp is already in replyToId, Slack auto-thread recovery returns no root thread and the route helper still picks the child timestamp before currentSessionKey, so the linked mirror-session sprawl can remain.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • remove merge-risk: 🚨 compatibility: Current PR review merge-risk labels are merge-risk: 🚨 session-state.

Label justifications:

  • P2: The PR targets a normal-priority Slack outbound delivery-mirror session bug with limited blast radius to Slack thread session state.
  • merge-risk: 🚨 session-state: The diff changes Slack reply/thread canonicalization and, if incomplete, can continue associating outbound mirror records with sibling thread sessions.
  • 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 includes focused tests, Testbox/CI, diff-check, and source-review evidence, but no redacted real Slack workspace or session-store after-fix output. 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 +3, Tests +27. Total +30 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 8 5 +3
Tests 1 27 0 +27
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 35 5 +30

What I checked:

Likely related people:

  • sandieman2: Authored recent merged reply-routing work that touched Slack channel, plugin type, and outbound threading surfaces adjacent to this bug. (role: recent reply-routing contributor; confidence: high; commits: c67dc59b02b0; files: extensions/slack/src/channel.ts, src/channels/plugins/types.core.ts, src/infra/outbound/message-action-threading.ts)
  • steipete: Current history shows Slack channel messaging seam work and prior Slack DM mirror-route canonicalization on the same file family. (role: historical Slack/session-route contributor; confidence: high; commits: ad97c581e21f, 50c77f23c1c1; files: extensions/slack/src/channel.ts, src/channels/plugins/types.core.ts)
  • Alex Knight: Authored the merged Mattermost thread-reply fix, the closest sibling pattern for preserving root thread routing for message-tool replies. (role: adjacent one-root transport contributor; confidence: medium; commits: 2365a137d88c; files: src/infra/outbound/message-action-threading.ts, extensions/mattermost/src/channel.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: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 27, 2026
@LiuwqGit

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@steipete steipete self-assigned this Jun 30, 2026
@steipete
steipete force-pushed the feat/issue-96535-slack-outbound-reply-delivery-mirror branch from 4ae0243 to 7f934f3 Compare June 30, 2026 23:08
@steipete

Copy link
Copy Markdown
Contributor

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@steipete

Copy link
Copy Markdown
Contributor

Land-ready maintainer verification for 7f934f3da32cd548d9b8c2796f30136aa79b5d98:

Known proof gap: no credentialed live Slack workspace replay was run. The reporter supplied deployment evidence, and the repaired owner path plus tri-state regression coverage directly proves the routing decision.

@steipete
steipete force-pushed the feat/issue-96535-slack-outbound-reply-delivery-mirror branch from 7f934f3 to adb1979 Compare June 30, 2026 23:19
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jun 30, 2026
@steipete
steipete merged commit 5e572dc into openclaw:main Jun 30, 2026
143 of 144 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 1, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 6, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 5e572dc)
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 8, 2026
…ies (openclaw#97168)

* fix(slack): prefer current thread session for inherited outbound replies to avoid delivery-mirror session sprawl

* fix(slack): require explicit replyToIsExplicit===false for current-session precedence

* fix(slack): canonicalize inherited thread replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 5e572dc)
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: 🚨 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.

Slack: each outbound reply persists a new single-message delivery-mirror session instead of reusing the conversation's thread session

2 participants