Skip to content

fix(integration-events): dedupe Slack thread-parent across messages+threads trees#175

Merged
kjgbot merged 1 commit into
mainfrom
fix/integration-event-dedup-logical-identity
Jun 9, 2026
Merged

fix(integration-events): dedupe Slack thread-parent across messages+threads trees#175
kjgbot merged 1 commit into
mainfrom
fix/integration-event-dedup-logical-identity

Conversation

@khaliqgant

Copy link
Copy Markdown
Member

Symptom

The operator saw the same Slack message echoing into the channel repeatedly. In integration-events.log, one message (1780946577_526789) appeared 59× — 25 received, 22 skipped (de-duped), but 3 injected (the visible echoes). The bot's own posts echoed too (compounded once the mounts went mirror and read the bot's own writebacks back down).

Root cause

A Slack thread parent (thread_ts == parent ts) materializes under two trees: the flat messages/<ts>/meta.json record AND the threads/<ts>/meta.json root — two distinct files, distinct revisions, but one logical message.

slackLogicalChangeFingerprint keyed them differently:

  • messages/<ts>/meta.jsonslack:channels:<ch>:message:<ts>:meta.json
  • threads/<ts>/meta.jsonslack:channels:<ch>:thread:<ts>:meta.json

Different fingerprints → different injection-dedupe keys → the same message injects once per tree, and re-injects as each tree's record is re-committed (revision churn). (event.type is the constant relayfile.changed and the separate messages/threads mounts yield the same dedupe path-tail, so the fingerprint was the only differentiator.)

Fix

Collapse the thread root (a threads/<ts> path with no replies/ segment) to the message identity in slackLogicalChangeFingerprint, so both materializations of the parent share one dedupe key and inject once. Thread replies keep their distinct :reply:<ts> identity and still inject.

One-line change in the fingerprint + a regression test.

Test

slack thread parent materialized under both messages and threads trees injects once — parent under both trees → 1 injection; a reply → still injects. 74/74 bridge tests pass (node --test src/main/__tests__/integration-event-bridge.test.ts).

Residual (follow-up, not in this PR)

An agent still receives one copy of its own outbound post, because mirror mounts read the bot's own messages back down and the bridge has no self-identity signal to suppress them. Fully killing self-echo needs the workspace's own bot-user id at the bridge — a separate change.

Source-only (bridge); activates on the next Pear rebuild+restart.

🤖 Generated with Claude Code

… trees

A Slack thread PARENT materializes under BOTH the flat messages/<ts> record
AND the threads/<ts> root (thread_ts == parent ts) — two distinct files with
distinct revisions, one logical message. slackLogicalChangeFingerprint keyed the
threads-root copy as `...:thread:<ts>:...` and the messages copy as
`...:message:<ts>:...`, so the two copies produced different injection-dedupe
keys and the same message was injected twice (and re-injected as each tree's
record was re-committed). Operator observed the same message echoing into the
channel repeatedly (one msg logged 59x received / 3x injected).

Fix: collapse the thread ROOT (no reply segment) to the message identity so both
materializations share one dedupe key and inject once. Thread REPLIES keep their
distinct `:reply:<ts>` identity. Regression test added; 74/74 bridge tests pass.

Note: an agent still receives ONE copy of its own outbound post (mirror reads it
back down); fully suppressing self-echo needs a self-bot-user signal — follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed.

Reviewed PR #175 against the current checkout. I did not find a validated breakage requiring code changes.

Local validation run:
npm test -- src/main/__tests__/integration-event-bridge.test.ts passed.

Final PR state observed:
mergeable=true, mergeable_state=clean; check runs complete with checks success and packaged-mcp-smoke success. Cubic returned neutral due to review limit.

Addressed comments

  • codeant-ai[bot]: raised only a quota-limit notice, no code finding to validate or fix.
  • gemini-code-assist[bot]: raised only a daily quota-limit notice, no code finding to validate or fix.
  • cubic · AI code reviewer: check output reported monthly review limit reached, no code finding to validate or fix.

@khaliqgant khaliqgant added the no-agent-relay-review Disable agent-relay automated PR review/fixes label Jun 9, 2026
@kjgbot kjgbot merged commit b67c201 into main Jun 9, 2026
4 checks passed
@kjgbot kjgbot deleted the fix/integration-event-dedup-logical-identity branch June 9, 2026 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-agent-relay-review Disable agent-relay automated PR review/fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants