test: cover quota-full and terminal reminder recovery

This commit is contained in:
dev-guard committed 2026-10-29 17:49:30 +00:00
1 parent 9cba975cd0
commit 2b0fa7000a
2 files changed
+106 -1

No files matched your search

@@ -736,3 +736,75 @@ describe("#1933: the first reminder waits a grace after the seat starts waiting"
expect((await run(new Date(NOW - GRACE_MS - 1_000).toISOString())).action).toBe("send");
});
});
describe("parked reminders after a positive no-input quota rejection", () => {
const quotaState = "guard-revision-a:100:8192";
const failedQuota = (result: Awaited<ReturnType<ReturnType<typeof makeParkedOwnerConsumerPolicy>["evaluate"]>>, phase: string | undefined = "not_reached") => {
const entry = sentHistory({ episodeKey: String(result.notes?.["episodeKey"]), primaryRow: String(result.notes?.["primaryRow"]), deliveryStatus: "failed", deliveryReason: "Held-message quota is full; no input was written." });
entry.evaluationNotes = { ...entry.evaluationNotes, deliveryCode: "retained_quota_full", ...(phase ? { deliveryInputPhase: phase } : {}) };
return entry;
};
const policy = (store: RowStore, history: WatchdogHistoryEntry[], state = quotaState, terminal = false) =>
makeParkedOwnerConsumerPolicy({ ...makeDeps([parkedSeat()], store, history), retentionState: () => state, isTerminalSeat: () => terminal } as ParkedOwnerConsumerDeps);
it("bounds unchanged attempts AND row bookkeeping across restart and history pruning", async () => {
const store = new RowStore();
const first = await policy(store, []).evaluate(makeJob());
expect(first.action).toBe("send");
const history = [failedQuota(first)];
const next = await policy(store, history).evaluate(makeJob());
expect(next).toMatchObject({ action: "skip", notes: { skippedSeats: [{ seat: SEAT, why: "retention-quota-unchanged" }] } });
const writes = store.appended.length;
const nudges = store.nudges.length;
expect(store.nudges[0]?.result).toContain("retained_quota_full");
expect(store.nudges[0]?.result).toContain("not_reached");
for (let i = 0; i < 100; i++) {
expect((await policy(store, i < 50 ? history : []).evaluate(makeJob())).action).toBe("skip");
}
expect(store.appended).toHaveLength(writes);
expect(store.nudges).toHaveLength(nudges);
expect(nudges).toBe(1);
});
it.each(["quota-released", "guard-revision-b:off"])("rechecks admission when %s changes", async state => {
const store = new RowStore();
const first = await policy(store, []).evaluate(makeJob());
const history = [failedQuota(first)];
expect((await policy(store, history).evaluate(makeJob())).action).toBe("skip");
const retry = await policy(store, history, state).evaluate(makeJob());
expect(retry.action).toBe("send");
const delivered = sentHistory({ episodeKey: String(retry.notes?.["episodeKey"]), primaryRow: String(retry.notes?.["primaryRow"]) });
expect((await policy(store, [delivered, ...history], state).evaluate(makeJob())).action).toBe("skip");
});
it.each([undefined, "attempted", "written"])("does not turn phase %s into positive no-input admission", async phase => {
const store = new RowStore();
const first = await policy(store, []).evaluate(makeJob());
const entry = failedQuota(first, phase);
if (phase === undefined) delete entry.evaluationNotes!["deliveryInputPhase"];
expect((await policy(store, [entry]).evaluate(makeJob())).action).toBe("send");
});
it("leaves an ordinary transient failure retryable", async () => {
const store = new RowStore();
const first = await policy(store, []).evaluate(makeJob());
const entry = failedQuota(first);
delete entry.evaluationNotes!["deliveryCode"];
expect((await policy(store, [entry]).evaluate(makeJob())).action).toBe("send");
});
it("records terminal-only custody once without reserving recurring input attempts", async () => {
const store = new RowStore();
const first = await policy(store, [], quotaState, true).evaluate(makeJob());
expect(first).toMatchObject({ action: "skip", notes: { skippedSeats: [{ seat: SEAT, why: "terminal-seat" }] } });
expect(store.nudges).toHaveLength(1);
expect(store.nudges[0]?.result).toContain("failed:terminal-seat:");
expect(store.nudges[0]?.result).toContain("route it to an agent seat");
const writes = store.appended.length;
for (let i = 0; i < 100; i++) expect((await policy(store, [], quotaState, true).evaluate(makeJob())).action).toBe("skip");
expect(store.appended).toHaveLength(writes);
expect(store.nudges).toHaveLength(1);
expect((await policy(store, [], quotaState, false).evaluate(makeJob())).action).toBe("send");
});
});
@@ -12,7 +12,7 @@ import { TmuxAdapter } from "../src/adapters/tmux.js";
import { ALL_MIGRATIONS } from "../src/db/all-migrations.js";
import { migrate } from "../src/db/migrate.js";
import type { ComposerSnapshot } from "../src/domain/composer-prompts.js";
import { OutboxHandler } from "../src/domain/outbox-handler.js";
import { OutboxHandler, OutboxHandlerError } from "../src/domain/outbox-handler.js";
import { RigRepository } from "../src/domain/rig-repository.js";
import { SeatDeliveryGuard, resolveGuardTarget } from "../src/domain/seat-delivery-guard.js";
import { SessionRegistry } from "../src/domain/session-registry.js";
@@ -368,3 +368,36 @@ describe("current parked-owner reminders", () => {
expect(f.submissions).toHaveLength(2);
});
});
describe("watchdog positive no-input outcomes", () => {
it("preserves the typed quota rejection through the real engine history", async () => {
const f = fixture();
const jobs = new WatchdogJobsRepository(f.db);
const history = new WatchdogHistoryLog(f.db);
const deliver = makeWatchdogDeliver({ sessionTransport: { send: async () => { throw new OutboxHandlerError("retained_quota_full", "Held-message quota is full; no input was written."); } } });
const engine = new WatchdogPolicyEngine({ jobsRepo: jobs, historyLog: history, eventBus: new EventBus(f.db), deliver });
const job = jobs.register({ policy: "periodic-reminder", targetSession: seat, intervalSeconds: 60, registeredBySession: seat, specYaml: "message: Still owed\n" });
await engine.evaluate(job);
expect(history.listForJob(job.jobId, 1)[0]).toMatchObject({ deliveryStatus: "failed", evaluationNotes: { deliveryCode: "retained_quota_full", deliveryInputPhase: "not_reached" } });
expect(f.pastes).toHaveLength(0);
});
it("does not infer no input from an untyped error with the same text", async () => {
const deliver = makeWatchdogDeliver({ sessionTransport: { send: async () => { throw new Error("Held-message quota is full; no input was written."); } } });
const result = await deliver({ targetSession: seat, message: "Still owed" }, { jobId: "quota-control", policy: PARKED_OWNER_POLICY_NAME, occurrenceId: "one" });
expect(result).not.toHaveProperty("code");
expect(result).not.toHaveProperty("inputPhase");
});
it("excludes bound terminal-only parked reminders while preserving other deliberate deliveries", async () => {
const sends = vi.fn().mockResolvedValue({ ok: true });
const deliver = makeWatchdogDeliver({ sessionTransport: { send: sends }, isTerminalSeat: () => true } as Parameters<typeof makeWatchdogDeliver>[0]);
const refused = await deliver({ targetSession: seat, message: "Parked wake" }, { jobId: "terminal-policy", policy: PARKED_OWNER_POLICY_NAME, occurrenceId: "one" });
expect(refused).toMatchObject({ status: "failed", code: "terminal_seat", inputPhase: "not_reached" });
expect(sends).not.toHaveBeenCalled();
const deliberate = await deliver({ targetSession: seat, message: "Explicitly requested terminal text" }, { jobId: "terminal-explicit", policy: "periodic-reminder", occurrenceId: "two" });
expect(deliberate.status).toBe("ok");
expect(sends).toHaveBeenCalledTimes(1);
});
});