fix(interactions): retain suppressed answer deliveries

This commit is contained in:
Dotta committed 2026-08-27 09:36:48 -05:00
1 parent 79c3783fff
commit 893b54212b
2 files changed
+52 -13

No files matched your search

@@ -250,6 +250,47 @@ describeEmbeddedPostgres("question response delivery", () => {
});
});
it("delivers after wake suppression outlasts the bounded error retry limit", async () => {
const seeded = await seed({ sourceStatus: "running" });
const fallbackRunId = randomUUID();
let wakeAttempts = 0;
const wakeup = vi.fn().mockImplementation(async () => {
wakeAttempts += 1;
if (wakeAttempts <= 5) return null;
return db.insert(heartbeatRuns).values({
id: fallbackRunId,
companyId: seeded.companyId,
agentId: seeded.agentId,
invocationSource: "automation",
status: "queued",
runtimeMode: "legacy",
driverKind: "codex",
contextSnapshot: { issueId: seeded.issueId },
}).returning().then((rows) => rows[0]!);
});
const service = questionResponseDeliveryService(db, {
heartbeat: { wakeup } as never,
steer: vi.fn(),
});
for (let attempt = 0; attempt < 5; attempt += 1) {
await expect(service.deliver(seeded.interaction.id)).resolves.toBeNull();
}
const delivered = await service.deliver(seeded.interaction.id);
expect(delivered).toMatchObject({
status: "fallback_queued",
mode: "wake_fallback",
targetRunId: fallbackRunId,
});
const [delivery] = await db.select().from(issueQuestionResponseDeliveries);
expect(delivery).toMatchObject({
status: "fallback_queued",
attemptCount: 6,
targetRunId: fallbackRunId,
});
});
it("reuses a durable wake receipt instead of issuing a duplicate continuation", async () => {
const seeded = await seed({ sourceStatus: "running" });
const [wakeRequest] = await db.insert(agentWakeupRequests).values({
@@ -365,9 +365,13 @@ export function questionResponseDeliveryService(
};
}
async function releaseForRetry(delivery: DeliveryRow, errorCode: string) {
async function releaseForRetry(
delivery: DeliveryRow,
errorCode: string,
options: { bounded: boolean } = { bounded: true },
) {
const at = now();
const exhausted = delivery.attemptCount >= MAX_DELIVERY_ATTEMPTS;
const exhausted = options.bounded && delivery.attemptCount >= MAX_DELIVERY_ATTEMPTS;
if (!exhausted) {
await db.update(issueQuestionResponseDeliveries).set({
status: "pending",
@@ -630,17 +634,11 @@ export function questionResponseDeliveryService(
}));
if (!wakeRun && !durableWake) {
const errorCode = "question_response_wake_skipped";
const exhausted = await releaseForRetry(claimed, errorCode);
if (!exhausted) return null;
return recordTerminal({
delivery: claimed,
interaction,
status: "failed",
mode: null,
targetRunId: null,
adapter,
errorCode,
});
// Scheduling suppression is an availability state, not a delivery
// failure. Keep the durable receipt retryable until the suppression is
// lifted; the bounded limit remains reserved for actual wake errors.
await releaseForRetry(claimed, errorCode, { bounded: false });
return null;
}
const targetRun = wakeRun ?? durableWake?.run ?? queuedSuccessor ?? null;
const coalesced = Boolean(queuedSuccessor && targetRun?.id === queuedSuccessor.id);