From 7bc938d7a3a8bfbbe8f62c4ca3de057d4c61e6f1 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 25 Oct 2026 12:25:39 -0500 Subject: [PATCH] docs: audit inbound wake notice migration ownership Co-Authored-By: Paperclip --- ...-10-25-inbound-wake-notices-sleep-audit.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 doc/plans/2026-10-25-inbound-wake-notices-sleep-audit.md diff --git a/doc/plans/2026-10-25-inbound-wake-notices-sleep-audit.md b/doc/plans/2026-10-25-inbound-wake-notices-sleep-audit.md new file mode 100644 index 00000000000..7b82f92c08d --- /dev/null +++ b/doc/plans/2026-10-25-inbound-wake-notices-sleep-audit.md @@ -0,0 +1,113 @@ +# Inbound wake notices: sleep migration audit + +Parent: #17639 at `34df8b0d0116a14d414a70d29f64a16c5672a335`. +Branch: `codex/sleep-30-inbound-wake-notices`. +Status: owner/producer audit in progress. Production still polls this duty. + +## One reason and its adjacent owners + +This slice owns the safe pre-run notices for admitted inbound chat messages: +queued, not started, and removed. The existing owner is +`chat-channels/inbound/wakeup-notices.ts`. These are projections of admission +receipts, not a second scheduler for executing the admitted task. + +`chat-channels.ts::processPublicationMaintenance` calls the notice scan on every +chat maintenance pass. The explicit/manual `processPendingPublications` API also +calls it when maintenance is enabled. Production queued-publication delivery +already has a maintenance-free path, which should stay independent. + +The current `enqueueInboundWakeupPublications` first invokes +`enqueueFailedChatRetryPublications`. That projects failed-run retry receipts, +with a separate authorization contract in `failed-run-retry.ts`. Separate this +adjacent reason rather than claiming both migrated by moving a compound call. +Terminal confirmation actions, interaction continuations, and GitHub maintenance +are additional separate reasons and remain pending. + +## Durable contract + +Existing `chat_actions`, `agent_wakeup_requests`, `chat_deliveries`, source +comments, publications and message links own the obligation. No new table is +needed. A normal inbound action must be processed or failed and have the exact +admission receipt. A coalesced receipt must resolve to a matching canonical owner. +Queued requires deferred execution without a run; a failed/cancelled/skipped +receipt must not be presented as a queue wait. + +A not-started/removed follow-up can retire only the exact previously visible +queued notice. The published provider message ID and outbound message link are +part of that proof. Deleting one coalesced source must not erase a surviving +batch's response. Promotion, questions, selected final answers and replacement +provider lanes retain precedence. Text remains the existing closed phrases: +no input text, internal scheduler failure, queue position, ETA or model content. + +`currentInboundWakeNotice` owns current issue, endpoint, identity and receipt +checks. It takes the existing nonblocking locks to avoid inversion with ingress +and scheduling. Preserve this authorization at projection and again at delivery. +A denied candidate is settled until a relevant mutation; lock contention is +unresolved work and needs a retry. The current catch treats both as zero inserted, +so a count-only return is insufficient once the permanent poll disappears. + +## Selector and lifecycle work required + +The current owner stores a process-global Date/UUID cursor between bounded calls. +That cursor belongs neither to a company nor to an issue and loses PostgreSQL +microsecond precision. Do not reuse it as a scoped worker cursor. Use stable SQL +precision and a complete per-pass continuation, advance past suppressed rows, +and preserve a concrete obligation when a page or lock remains unfinished. + +Use the existing scoped-deadline worker. Startup reconstructs saved work; scoped +commits refresh only matching work; empty or settled history disarms. Retry only +contention, failed reconciliation and unfinished batches. Changes during a pass, +first writes during drain, failed startup, resume and shutdown require the same +shared lifecycle contract as earlier slices. + +## Producer audit: do not remove the poll yet + +- `inbound/wakeups.ts` settles actual accepted actions. It signals lifecycle + projection for processed actions, but failed actions can also change notices. + Preserve the distinction between durable admission and a callback returning. +- `heartbeat/queue.ts` creates deferred receipts, coalesces requests, promotes + owners and changes terminal receipt status. Existing milestone signals cover + some run transitions, not every receipt-only mutation. Inspect the existing + writer transaction owners before adding a notification contract. +- `execution-wait.ts` can persist skipped admission without creating a run. + Determine which transitions can create or retire an eligible notice. +- `issue-tree-control.ts` cancels queued/deferred receipts without runs. + Its current notification is for terminal workspace cleanup, not these notices. +- `chat-channels/slack/session-stop.ts` can cancel a queued receipt. A signal for + the stop-action worker is not automatically a notice wake. +- `companies.ts` cancels queued/deferred/claimed receipts during company state + changes. Preserve company visibility and disablement behavior. +- Source-comment removal and later receipt promotion can retire a visible notice; + audit `issues.ts`, message lifecycle owners and queued-comment control paths. +- Publication success/message-link persistence can make a retirement eligible. + The existing issue-scoped publication notification is a possible shared + invalidation boundary; ordinary lease/claim bookkeeping must remain narrow. +- Endpoint, connection, resource, identity, membership and task visibility can + restore authority. Reuse the existing eligibility contract only after checking + these notice predicates; lifecycle projection coverage is not proof by itself. +- Remaining production receipt writers include heartbeat run control/lifecycle, + retries, recovery, task retry, human-directed work, native status decisions, + continuations and provider-capacity retry. Classify supported notice-producing + paths explicitly. A search hit alone is neither coverage nor a required wake. + +Notifications must precede the write and follow the existing outer transaction. +Do not create an external call or a recovery object at every route. Do not wake +for unchanged health checks or claim renewal. Process-local signals retain the +single active writer model; external writers require their own integration. + +## Acceptance + +Existing integration fixtures `deferredChatQueueFixture` and its pre-run FIFO +cases exercise coalescing, restart, successor reuse, exact final/question lanes, +revoked reach, source deletion, identity/session changes, canonical owner IDs, +and receipt-lock contention. Establish that baseline, then add real-producer +worker tests with no manual projection call or periodic backstop. + +New cases must prove configured-idle/history quiet, first durable admission, +outer commit/rollback, receipt-only cancellation, source removal after promotion, +visible-queue receipt ordering, restoration of authority, suppressed full pages, +scoping/fairness, lock retries, drain/resume, concurrent changes and shutdown. +Do not turn queued work into periodic notice polling after its notice is saved. + +This audit changes no runtime behavior or Cloud eligibility. Exact implementation, +producer coverage and acceptance remain required before a migration PR is ready.