Skip to content

feat(slack): let the session lifecycle acknowledge a turn, reacting only when it could not - #2134

Merged
zfy0701 merged 1 commit into
agentconnect-md:mainfrom
joerideturck:fix/slack-skip-seen-reaction
Sep 17, 2026
Merged

zfy0701 merged 1 commit into
agentconnect-md:mainfrom
joerideturck:fix/slack-skip-seen-reaction

Conversation

@joerideturck

@joerideturck joerideturck commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A behaviour change, with the product convention changed alongside it — revised after the first review, which rightly pointed out that the earlier version contradicted docs/product-conventions.md and could leave a turn with no acknowledgement at all.

"A trigger is acknowledged before it is answered" had every platform place a 👀 reaction on the triggering message (#1537). On Slack the daemon already produces a durable turn-start signal: the agent-session lifecycle (agents.sessions.setStatus) marks the thread "is working…" with the Stop control for the whole turn and withdraws it when the turn ends. A Slack turn therefore showed two acknowledgements, and the one that never goes away stayed on every answered message — in workspaces where 👀 already carries a meaning between people (ours uses it as "someone is looking at this"), that collides and accumulates.

Convention change

docs/product-conventions.md, same section, now says: the reaction is the default acknowledgement on every platform; on Slack, where Slack confirms it took the processing write, the lifecycle indicator is the acknowledgement and no reaction is placed; only when that write is refused (missing scope, API failure, no indicator for this turn) does the reaction fall back in, so a Slack turn never shows neither. A typing hint is not an indicator in this sense — Telegram and Discord react as before. The "acknowledgement, not a status" paragraph is unchanged for the reaction itself.

Implementation

  • SlackConnection.setStatus / setSessionLifecycle resolve true when Slack accepted the write or the slot already showed that state (dedupe hit), false when the call failed. The failure is still absorbed exactly as before — it is only reported now, which answers the second review finding: method presence is no longer what gates the reaction, the API outcome is.
  • showActivity returns that outcome for Slack and false for every transient indicator (typing hints, no connection). Its other callers ignore the promise as they always did.
  • Turn start hands the outcome to acknowledgeTrigger, which on Slack reacts only when it resolves false or rejects, and elsewhere reacts at once. Code-host turns and the agent-callable reaction tool are untouched.

Testing

packages/daemon:

  • test/daemon-trigger-ack.test.ts — Telegram/Discord/cron/hook cases unchanged and passing. New: a Slack DM whose lifecycle write is taken places no reaction; one whose write is refused falls back to the reaction; one whose write throws falls back too; Telegram still reacts even when its (typing) indicator was shown.
  • test/connection.test.ts — setStatus reports true on success and on a dedupe hit, false on failure; the existing "keeps a failing lifecycle call out of dispatch" case now asserts false instead of undefined.
  • Full daemon suite locally: 7023 passed; the 14 failures are the sandbox/bwrap, ACP-matrix and pod-volume cases that need tooling this macOS host lacks and fail identically on an unmodified checkout. tsc --noEmit clean.

Running on our deployment since 2026-09-17 in its earlier form; this revision goes out next.

🤖 Generated with Claude Code

@joerideturck
joerideturck force-pushed the fix/slack-skip-seen-reaction branch from eff4909 to 24f0b19 Compare September 17, 2026 09:44

@agentconnect-md-test agentconnect-md-test 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.

Requesting changes for one user-facing regression. The new Slack early return removes the durable 👀 acknowledgement from every normal Slack turn, contrary to docs/product-conventions.md (“A trigger is acknowledged before it is answered”), which explicitly requires every real inbound message to receive that reaction across chat platforms and distinguishes it from Slack’s transient status. The lifecycle path is also best-effort: SlackConnection.setStatus() catches API failures, so checking only that the method exists can leave the user with neither the working indicator nor the acknowledgement. Please preserve the reaction (and the existing cross-platform contract), or revise the authoritative product/design contract as part of an intentional product change before changing this behavior.

Validation: git diff --check passed. The focused daemon test did not reach execution because the clean runner began materializing the full workspace dependency tree; I stopped that setup rather than treating it as a project failure.

sent by review-bot (Codex · gpt-5.6-sol) · open in session

Comment thread packages/daemon/src/daemon.ts Outdated
@joerideturck joerideturck changed the title fix(slack): place no turn-start reaction where the session lifecycle already shows the turn feat(slack): skip the turn-start reaction where the session lifecycle already shows the turn Sep 17, 2026
@joerideturck
joerideturck force-pushed the fix/slack-skip-seen-reaction branch from 24f0b19 to 71db554 Compare September 17, 2026 12:44

@agentconnect-md-test agentconnect-md-test 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.

Requesting changes because the prior blocking finding remains unresolved. This revision’s tree is byte-for-byte identical to 24f0b19b; only the commit subject/message now calls the change a feature. docs/product-conventions.md is still authoritative and still requires every turn with a real inbound message to receive the durable 👀 acknowledgement across chat platforms, explicitly distinguishing that acknowledgement from Slack’s transient status. The implementation also checks only whether setStatus exists, while SlackConnection.setStatus() deliberately absorbs API failures, so a turn can show neither signal. Please preserve the Slack reaction, or update the authoritative product/design contract and implement the intended fallback semantics as part of the behavior change.

Validation: the trusted merge has exactly the stated base/head parents, the new and previous head trees are identical, and git diff --check passes.

sent by review-bot (Codex · gpt-5.6-sol) · open in session

Comment thread packages/daemon/src/daemon.ts Outdated
@zfy0701

zfy0701 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

@joerideturck I think this make sense, could you also ask agent to update docs/product-conventions.md to make slack an exception in doc

…nly when it could not

A behaviour change, with the product convention changed alongside it. "A trigger is
acknowledged before it is answered" (docs/product-conventions.md) had every platform place
a 👀 reaction on the triggering message (agentconnect-md#1537). On Slack the daemon already produces a
durable turn-start signal — the agent-session lifecycle marks the thread "is working…" with
the Stop control for the whole turn and withdraws it when the turn ends — so a Slack turn
showed two acknowledgements, and the one that never goes away stayed on every answered
message, in workspaces where 👀 already carries a meaning between people.

The convention now reads: the reaction is the default acknowledgement, and Slack's lifecycle
indicator IS the acknowledgement where Slack confirms it took the `processing` write; the
reaction falls back in only when that write was refused, so a Slack turn never shows neither.
A typing hint is not an indicator in this sense, so Telegram and Discord react as before, and
code-host turns are untouched.

Implementation: `SlackConnection.setStatus` (and `setSessionLifecycle`) now resolve `true`
when Slack accepted the write or the slot already showed that state, `false` when the call
failed — the failure is still absorbed, only reported. `showActivity` returns that outcome
for Slack and `false` for every transient indicator; the turn start hands it to
`acknowledgeTrigger`, which on Slack reacts only when it resolves `false` or rejects, and
elsewhere reacts at once as before. Other `showActivity` callers ignore the promise as they
always did.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@joerideturck
joerideturck force-pushed the fix/slack-skip-seen-reaction branch from 71db554 to 8476235 Compare September 17, 2026 13:38
@joerideturck joerideturck changed the title feat(slack): skip the turn-start reaction where the session lifecycle already shows the turn feat(slack): let the session lifecycle acknowledge a turn, reacting only when it could not Sep 17, 2026
@joerideturck

Copy link
Copy Markdown
Contributor Author

Revised in 8476235 to address both findings:

  1. Contract. docs/product-conventions.md ("A trigger is acknowledged before it is answered") is updated in this PR: the reaction stays the default acknowledgement everywhere; on Slack a lifecycle write Slack confirms it took is the acknowledgement, and the reaction is the fallback when that write is refused.
  2. Fallback semantics. setStatus now reports whether Slack accepted the write (true, including a dedupe hit) or the call failed (false, failure still absorbed). The turn start hands that outcome to acknowledgeTrigger, which on Slack reacts only when it is false or the promise rejects. Method presence no longer gates anything, so a turn cannot end up with neither signal. Tests cover taken / refused / throwing writes and the Telegram typing-hint case.

@agentconnect-md-test agentconnect-md-test 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.

Approved. This revision resolves both earlier blockers: the authoritative product convention now explicitly defines a confirmed Slack lifecycle write as the turn acknowledgement, and SlackConnection.setStatus() reports success/failure so a refused or rejected lifecycle write falls back to the 👀 reaction instead of leaving the turn silent. Telegram, Discord, and code-host acknowledgement behavior remains intact. The added tests cover lifecycle success, dedupe, failure, rejection fallback, and the non-Slack path.

Validation: the trusted checkout is the merge of the stated base and head, git diff --check passes, and the current Build, Unit Test, Windows unit, Evaluation, Daemon Store, and Sandbox checks pass. The integration job was still pending at review time.

sent by review-bot (Codex · gpt-5.6-sol) · open in session

@zfy0701
zfy0701 merged commit 416e400 into agentconnect-md:main Sep 17, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants