diff --git a/server/src/__tests__/productivity-review-service.test.ts b/server/src/__tests__/productivity-review-service.test.ts index 602267d44e..0c6ed8b413 100644 --- a/server/src/__tests__/productivity-review-service.test.ts +++ b/server/src/__tests__/productivity-review-service.test.ts @@ -497,6 +497,35 @@ describeEmbeddedPostgres("productivity review service", () => { expect(reviews).toHaveLength(1); }); + it("treats a recently cancelled review as a snooze window", async () => { + const now = new Date("2026-04-28T12:00:00.000Z"); + const seeded = await seedAssignedIssue(); + await insertRuns({ + companyId: seeded.companyId, + agentId: seeded.coderId, + issueId: seeded.issueId, + count: 10, + now, + }); + const service = productivityReviewService(db); + await service.reconcileProductivityReviews({ now, companyId: seeded.companyId }); + const [review] = await listProductivityReviews(seeded.companyId); + await db + .update(issues) + .set({ status: "cancelled", updatedAt: now }) + .where(eq(issues.id, review!.id)); + + const result = await service.reconcileProductivityReviews({ + now: new Date(now.getTime() + 30 * 60 * 1000), + companyId: seeded.companyId, + }); + const reviews = await listProductivityReviews(seeded.companyId); + + expect(result.snoozed).toBe(1); + expect(result.created).toBe(0); + expect(reviews).toHaveLength(1); + }); + it("reports and logs soft-stop holds for open no-comment reviews", async () => { const now = new Date("2026-04-28T12:00:00.000Z"); const seeded = await seedAssignedIssue(); diff --git a/server/src/services/productivity-review.ts b/server/src/services/productivity-review.ts index cae4e37668..8441c5c675 100644 --- a/server/src/services/productivity-review.ts +++ b/server/src/services/productivity-review.ts @@ -260,7 +260,7 @@ export function productivityReviewService(db: Db, deps?: { enqueueWakeup?: Enque .then((rows) => rows[0] ?? null); } - async function findRecentResolvedProductivityReview( + async function findRecentTerminalProductivityReview( companyId: string, sourceIssueId: string, thresholds: ProductivityReviewThresholds, @@ -275,7 +275,7 @@ export function productivityReviewService(db: Db, deps?: { enqueueWakeup?: Enque eq(issues.companyId, companyId), eq(issues.originKind, PRODUCTIVITY_REVIEW_ORIGIN_KIND), eq(issues.originId, sourceIssueId), - eq(issues.status, "done"), + inArray(issues.status, ["done", "cancelled"]), gt(issues.updatedAt, cutoff), ), ) @@ -807,7 +807,7 @@ export function productivityReviewService(db: Db, deps?: { enqueueWakeup?: Enque result.skipped += 1; continue; } - if (await findRecentResolvedProductivityReview(candidate.companyId, candidate.id, thresholds, now)) { + if (await findRecentTerminalProductivityReview(candidate.companyId, candidate.id, thresholds, now)) { result.snoozed += 1; continue; }