-- v1.1.1: abandoned_executions — design notes §3 #9. -- -- Forensic table for the "dispatcher tried to resolve a oneshot inbox -- but the receiver was already dropped" edge case. The orchestrator -- timed out (returned 504 to the caller) and gave up on the channel, -- but then the dispatcher's execution succeeded later. The caller -- never sees the result; the row exists so the operator can -- correlate when the abandoned-counter metric spikes. -- -- Only the dispatcher-after-orchestrator-timeout edge case writes -- here; ordinary "script timed out, caller got 504" stays uneventful. -- -- 7-day retention, GC by `created_at`, sweep alongside dead_letters. CREATE TABLE abandoned_executions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_id UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE, -- Original outbox row id (the row itself has been deleted). outbox_id UUID NOT NULL, script_id UUID, -- The inbox channel id the dispatcher tried to resolve. inbox_id UUID NOT NULL, -- The HTTP status code the dispatcher attempted to send back. status_code INT NOT NULL, -- Truncated body / error description (capped at write time — -- the dispatcher doesn't need to ship megabytes here). result_summary TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_abandoned_executions_gc ON abandoned_executions (created_at);