feat(slack): Recover durable reply delivery#960
Conversation
Make ConversationEvent the sole application history contract and derive Pi context through an adapter. Rename the SQL table in place, version rows, and rewrite legacy Pi messages through a bounded upgrade migration. Keep only rollout-scoped database and Redis import compatibility while removing the legacy application store and projection paths. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Move provider-neutral event reduction and runtime-context shaping into the conversations domain. Keep the Pi adapter responsible only for validating projected model messages. Build reporting transcripts, activity, and child history from canonical events while preserving lazy legacy import, privacy gates, redaction, and the existing API contract. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Persist visible message, metadata, and reply facts in the canonical conversation event log while maintaining the SQL message table as an atomic hydration and search projection. Add idempotent event keys, shared sequence locking, a bounded zero-gap backfill, and the stopped-worker schema cut that removes the legacy step compatibility view. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Project runtime hydration, reporting, and eval transcripts from canonical visible-message events while retaining safe terminal outcome and trace metadata. Remove message-table transcript readers and make assistant message identities stable across persistence retries. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Add strict start and first-terminal-wins outcome facts, then wire the local runtime through durable input, delivery, and persistence boundaries. Surface privacy-safe lifecycle failures in conversation detail, preserve incident correlation, and remove synthetic no-reply transcript messages. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Project canonical conversation history into a strict ordered API boundary. Keep visible content singular and omit internal payloads so reporting consumers can adapt one event stream safely. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Make canonical conversation events the only detail and dashboard history authority. Remove transcript and subagent compatibility APIs, inherit child privacy from the locked root, and make retention purge whole trees without append races. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Make SQL events the only live history source for Pi projection and visible hydration. Keep the Redis decoder and backfill writer available only through the explicit operator upgrade path. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Document canonical metadata, non-history aggregates, completed event reporting work, and the bounded migration and delivery follow-ups that remain intentionally open. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Keep the canonical-event mock route assertion aligned with the 365-day profile history introduced on main. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Exclude parent-linked mock conversations at the feed boundary and preserve public location links in projected summaries. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Select root conversations by identity and verify imported advisor transcripts through the parent event and ordinary child detail route. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Pair repeated imported advisor lifecycles through public event sequence metadata. Keep private invocation identifiers internal, reduce the dashboard view to one authoritative status, and allow incomplete child histories to remain navigable. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Treat every model projection as conversation-local now that shared child history no longer exists. Simplify turn cursor conversion around the canonical event sequence. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Trace immutable parent links once and remove duplicate topology validation. Preserve root-derived privacy and event-lock fencing, then reread activity after locks so retention never acts on a stale child timestamp. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Consume only the privacy-safe semantic events exposed by the conversation API, and delete the legacy Pi payload rendering universe. Keep canonical subagent lifecycle status visible and searchable. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep the accessible contribution-grid tooltip assertion added on main while dropping obsolete transcript inspector cases during the event-history cutover. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Exclude the fixed started label from transcript search so common substrings do not retain every tool row. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep the unreleased event, visible-message, and delivery schema changes in one migration. This removes intermediate snapshots that have no deployed upgrade boundary while preserving the final schema. Co-Authored-By: GPT-5 Codex <noreply@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.
b063024 to
5732e22
Compare


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.