fix(linear-slack): stop the receipt-wait from eating replies; add a fast 👀 ack#55
Conversation
…ast ack Live trace of a real run: the issue WAS created (AR-87 in Launch SDK) but no reply ever posted. Two causes, one of them self-inflicted by #53: - The writeback receipt timeout defaults to 3s — too short for the cloud worker's round-trip. `slack.post()` returned `ts:''` and `createIssue()` returned the draft-path fallback even though both writes landed. - #53 made `postReply` THROW on an empty `ts`. With the scope mounted the draft still flushes at cleanup, so an empty `ts` is a slow receipt, not a drop — but the throw crashed the turn and tore the box down before the reply flushed. Issue created, channel silent. Fixes: - Build slack/linear clients with `writebackTimeoutMs: 12s` so receipts have time to arrive (keeps the box alive to flush) and we get real confirmation. - `postReply` no longer throws on a missing receipt — it logs `linear-slack.reply.no-receipt` and lets the draft flush. The genuine no-scope failure is already caught at build time by tests/persona-integration-scopes, not at runtime. - Reframe an unconfirmed create/comment from a scary "never confirmed" to "submitting now — appears on the board shortly" (creates land via the mirror within ~minutes), still logged for triage. - Add a fast 👀 ack: react on the teammate's message the moment the handler picks up the turn (fire-and-forget, doesn't block the harness), so the channel isn't silent for the minutes-long run. Follow-up to #53/#54. Refs AgentWorkforce/cloud#2029. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI. |
There was a problem hiding this comment.
Code Review
This pull request introduces an immediate reaction acknowledgement ('eyes' emoji) to Slack messages, increases the writeback timeout to 12 seconds to accommodate cloud worker round-trips, and refactors error handling so that missing writeback receipts result in warnings rather than thrown errors or critical failures. Feedback on these changes suggests wrapping the fire-and-forget reaction in Promise.resolve().then(...) to prevent synchronous exceptions from crashing the handler, and restoring a try/catch block around the fallback postReply call in the error path to avoid masking the original harness error.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| void Promise.resolve(slack.react(msg.channel, msg.ts, ACK_EMOJI)).catch((err) => | ||
| ctx.log?.('warn', 'linear-slack.ack.failed', { error: errorMessage(err) }), | ||
| ); |
There was a problem hiding this comment.
If slack.react throws synchronously (for example, if the method is missing at runtime due to a dependency mismatch, or if it performs synchronous argument validation), it will throw before Promise.resolve or .catch can intercept it. This would crash the entire handler turn, violating the 'never fail the turn over a reaction' goal. Wrapping the call using Promise.resolve().then(...) ensures that both synchronous and asynchronous errors are safely caught.
| void Promise.resolve(slack.react(msg.channel, msg.ts, ACK_EMOJI)).catch((err) => | |
| ctx.log?.('warn', 'linear-slack.ack.failed', { error: errorMessage(err) }), | |
| ); | |
| void Promise.resolve() | |
| .then(() => slack.react(msg.channel, msg.ts, ACK_EMOJI)) | |
| .catch((err) => | |
| ctx.log?.('warn', 'linear-slack.ack.failed', { error: errorMessage(err) }), | |
| ); |
| } catch (postErr) { | ||
| ctx.log?.('error', 'linear-slack.reply.undelivered', { error: errorMessage(postErr) }); | ||
| } | ||
| await postReply(ctx, slack, msg, reply); |
There was a problem hiding this comment.
Although postReply no longer throws on a missing receipt, it can still throw other errors (e.g., network failures or API errors). If postReply throws inside this error handler, it will propagate and mask the original harness error (err), making debugging and triage significantly harder. Wrapping it in a try/catch block ensures the original failure remains the primary reported error.
try {
await postReply(ctx, slack, msg, reply);
} catch (postErr) {
ctx.log?.('error', 'linear-slack.reply.undelivered', { error: errorMessage(postErr) });
}|
pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
ℹ️ 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. pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR strengthens Slack/Linear agent writeback by adding immediate Slack acknowledgment reactions, extending writeback timeouts, and gracefully handling missing delivery receipts without throwing errors. It also refines Linear action messaging to distinguish confirmed vs. pending states when receipts are unavailable. ChangesSlack/Linear Agent Writeback Reliability
Sequence DiagramsequenceDiagram
participant IncomingMsg as Incoming Slack Message
participant Handler as Handler
participant Slack as Slack API
participant Linear as Linear API
participant Result as Result Reply
IncomingMsg->>Handler: Message received (handleSlackEvent)
Handler->>Slack: react(...) → acknowledge with emoji
note over Slack: Fire-and-forget, logged if fails
Handler->>Linear: Execute action (create_issue/comment)
Linear-->>Handler: Response with receipt (or not)
Handler->>Handler: confirm() → check if http(s) receipt
alt Receipt URL found
Handler->>Result: Confirmed message
else No receipt or non-http URL
Handler->>Result: Pending message (warn, don't error)
end
Handler->>Slack: postReply(...) with result
note over Slack: Log warning if no receipt, continue
Slack-->>Handler: Delivery result
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
ℹ️ 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. pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
1 similar comment
|
pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
ℹ️ 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. pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
|
ℹ️ 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. pr-reviewer could not complete review for #55 in AgentWorkforce/agents. |
What the live trace showed
After #53/#54 deployed, a real run still posted nothing — but the issue was created (
AR-87in Launch SDK). The harness side is perfect now (resolved team/project ids, emitted a cleanlinear-actionsblock). The failure was entirely in the handler's writeback handling:slack.post()returnedts:''andcreateIssue()returned the draft-path fallback even though both writes landed (AR-87 materialized ~4 min later via the 30s mirror sync).ts" backfired. With the/slack/channelsscope mounted, the draft flushes at box cleanup — so an emptytsis a slow receipt, not a drop. But the throw crashed the turn and tore the box down before the reply flushed. Issue created, channel silent.Fixes
writebackTimeoutMs: 12s, so receipts have time to arrive (and the box stays alive to flush) and we get real confirmation (alinear.appurl / a realts).postReplyno longer throws on a missing receipt — it logslinear-slack.reply.no-receiptand lets the draft flush. The genuine "nothing mounts /slack" failure is caught at build time bytests/persona-integration-scopes, so it doesn't need a turn-crashing runtime throw.Test
npm test60/60, typecheck clean. Updated the unconfirmed-create test for the new message.Follow-up to #53/#54. Framework-level receipt reliability tracked in AgentWorkforce/cloud#2029.
🤖 Generated with Claude Code
Summary by cubic
Stops
linear-slackfrom dropping replies by waiting longer for writeback receipts and not throwing on slow receipts. Adds a fast 👀 reaction so users see an immediate ack.Bug Fixes
writebackTimeoutMs: 12_000to allow receipts to arrive.postReplyno longer throws on emptyts; logslinear-slack.reply.no-receiptand lets the draft flush.linear-slack.action.unconfirmed.New Features
Written for commit 7a5fac9. Summary will update on new commits.