feat(slack): Recover durable reply delivery#960
Conversation
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Make the canonical delivery outbox own ordinary Slack reply completion across process crashes and ambiguous provider writes. Recover older deliveries before new input, reconcile exact Slack receipts, and keep terminal transcript facts authoritative when runtime-state repair fails. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Keep unresolved Slack delivery in the pending outbox and use the canonical turn terminal as the only durable outcome. This removes duplicate history events while preserving ambiguous-commit recovery through deterministic finalized-message evidence. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Model sequential Slack delivery as ordered receipts plus one current state, enforce one unresolved delivery per conversation, and remove speculative part identities, retry indexes, and timestamps. Keep ambiguous writes unrepostable and back off permanent reconciliation failures at the established provider maximum. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Avoid coupling the recoverable delivery service to Slack infrastructure for a retry constant while retaining the established one-hour operator-recovery cadence. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Remove mocked branch matrices and duplicate orchestration assertions while retaining event ordering, delivery repair, command identity, privacy, and intentional-silence contracts at their strongest test layers. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Move delivery commands, outbox storage, and recovery behind the Slack boundary. Wire resumed and dispatched runtimes to the shared durable delivery service without replaying transcript-core cutover changes.
Use the SQL-backed turn lifecycle across eval runtime, scheduled dispatch, and OAuth callback fixtures. Normalize actual scheduled Slack delivery and omit sendMessage when internal dispatch owns the final post. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Route non-live final replies through the SQL outbox so callback retries reconcile delivery without rerunning the model or posting duplicates. Repair derived resume state from canonical terminal facts after process loss. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Advance due delivery intents from heartbeat and route dispatch delivery retries through dedicated callbacks without consuming model attempts. Repair session and thread state before outbox deletion so crashes cannot strand resumed or dispatched replies. Handler gates recover canonical terminals without rerunning Pi. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Persist exact authorization completion candidates so busy OAuth callbacks can be recovered autonomously without racing session state. Keep canonical dispatch terminals recoverable through final-attempt projection failures, including side-effect-only turns, and schedule completed plugins after recoverable resume delivery. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Persist an inert auth recovery intent before one-time code exchange and promote it only after an exact credential receipt is committed. Recover canonical dispatch outcomes before expiry or attempt limits. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Use the existing turn-session record and summary index for prepared authorization recovery. Remove the parallel candidate registry, lock, capacity, and pruning protocol while preserving credential receipt crash safety. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Do not consume a one-time authorization code when an awaiting auth session changes before its recovery intent is durably indexed. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Derive one opaque receipt per authorization attempt so concurrent callbacks cannot strand recovery. Clean exact terminal processing state from stale continuation deliveries. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Remove the exact pending-auth marker when a newer user message abandons its parked session, including after callback task loss. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Size dispatch ownership from the configured host window and serialize conversation lease renewals so slow check-ins cannot overlap or outlive worker teardown. Co-Authored-By: GPT-5 Codex <codex@openai.com>
Record canonical turn failures when parked Slack runs are abandoned, cannot materialize, or terminally strand. Fence continuation failures by exact session version and require lifecycle injection so stale workers cannot overwrite successful completion. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Resolve one numeric function duration during Nitro setup, emit it to Vercel and runtime config, and inject it into dispatch execution so custom host windows cannot outlive ownership. Co-Authored-By: GPT-5 Codex <codex@openai.com>
Serialize parked-session terminal transitions with the active resume lock and route stranded failure replies through durable Slack recovery. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Drive the dispatch agent timeout, absolute deadline, lease, and Vercel route rule from the same injected function duration. Co-authored-by: GPT-5 Codex <codex@openai.com>
Serialize OAuth activation and Slack delivery terminal repair with active conversation ownership. Keep credential receipts recoverable, centralize session and thread repair, and preserve resumed slice metrics across heartbeat recovery. Co-Authored-By: GPT-5 Codex <codex@openai.com>
Acquire exact conversation ownership before consuming MCP authorization codes and retain it through credential receipt and recovery activation. Preserve terminal slice metrics and wire eval callbacks through the shared recoverable delivery. Co-Authored-By: GPT-5 Codex <codex@openai.com>
Load only the containing event epoch through the committed sequence cursor when rebuilding a durable Slack delivery.
Acknowledge durable reply input before completion notifications run so post-delivery callback work cannot leave the message pending.
Tokenize the flexible shell command before matching the eval identity endpoint so URL prefixes or suffixes cannot satisfy the assertion. Co-Authored-By: GPT-5 Codex <codex@openai.com>
Match durable OAuth workflows by endpoint and successful output and allow the established extended timeout. Keep generic transcript rubric visibility unchanged.
Make the dispatch recovery integration test own the callback URL it requires instead of relying on a developer env file that is absent in CI.
Regenerate the standalone 0006 outbox migration from finalized core 0005, including the due-delivery index. Keep the OAuth identity eval matched to the exact endpoint token.
Keep delivery terminal authority on turn events and attempt the safe Slack fallback when resumed failure-state persistence fails. Retain the persistence error after successful fallback delivery.
Enumerate prepared OAuth recoveries from a dedicated TTL-bound state index instead of the capped operational turn feed. Remove exact entries after a durable wake or terminal session transition.
Reject new recovery identities when the durable index reaches capacity, while allowing exact retries. Prune incomplete registrations after a five-minute grace period and reject malformed persisted indexes without unbounded parsing.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 31aa235. Configure here.
| await markDispatch({ | ||
| dispatch, | ||
| ...canonicalTerminalProjection, | ||
| }); |
There was a problem hiding this comment.
Dispatch status flips on retry
Medium Severity
The delivered-marker repair path writes a persistence_failed terminal and marks the dispatch completed, but a later callback that reads that same terminal through priorTerminal sets status from modelSucceeded, which is false for any turn_failed. A crash between the lifecycle write and markDispatch therefore projects the same delivered reply as failed on retry.
Reviewed by Cursor Bugbot for commit 31aa235. Configure here.


Slack replies now persist a delivery intent before the provider write and recover uncertain or interrupted delivery through one SQL outbox. Ordinary Slack turns, continuations, OAuth resumes, and internal dispatches share the same post/reconcile/terminal path; accepted multipart replies commit the accepted prefix once, while rejected output is rolled back without discarding already committed model or tool continuity.
Heartbeat recovery claims due work with bounded leases and projection reads, then uses first-terminal-wins lifecycle updates to avoid duplicate completion or failure. Reconciliation never blindly reposts an ambiguous write. Background post and reconciliation resolve the Slack installation from the persisted destination workspace, so recovery remains correctly scoped in multi-workspace deployments.
Authorization callbacks register an exact recovery identity before consuming a one-time code. A separate TTL-bound, capacity-bounded index makes committed callback gaps enumerable without relying on the capped operational session feed; exact retries are idempotent, overflow fails before code exchange, and entries clear after durable wake or terminalization.
This PR stacks on #916. It leaves the canonical conversation-event migration
0005byte-for-byte unchanged and adds only0006_pending_delivery_outboxfor the outbox table and due-work index. Review should start with the outbox and recoverable delivery service, then the three runtime integrations, and finally callback/heartbeat fencing.