feat(slack): let the session lifecycle acknowledge a turn, reacting only when it could not - #2134
Conversation
eff4909 to
24f0b19
Compare
There was a problem hiding this comment.
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
24f0b19 to
71db554
Compare
There was a problem hiding this comment.
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
|
@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>
71db554 to
8476235
Compare
|
Revised in 8476235 to address both findings:
|
There was a problem hiding this comment.
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
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.mdand 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 theprocessingwrite, 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/setSessionLifecycleresolvetruewhen Slack accepted the write or the slot already showed that state (dedupe hit),falsewhen 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.showActivityreturns that outcome for Slack andfalsefor every transient indicator (typing hints, no connection). Its other callers ignore the promise as they always did.acknowledgeTrigger, which on Slack reacts only when it resolvesfalseor 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—setStatusreportstrueon success and on a dedupe hit,falseon failure; the existing "keeps a failing lifecycle call out of dispatch" case now assertsfalseinstead ofundefined.tsc --noEmitclean.Running on our deployment since 2026-09-17 in its earlier form; this revision goes out next.
🤖 Generated with Claude Code