From bcf25832d0ffae25890b2ee7eed112d1c2d114fe Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 1 May 2026 08:06:05 -0500 Subject: [PATCH] Address productivity review feedback Co-Authored-By: Paperclip --- .../productivity-review-service.test.ts | 44 ++++++++++++++++++- server/src/services/productivity-review.ts | 3 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/server/src/__tests__/productivity-review-service.test.ts b/server/src/__tests__/productivity-review-service.test.ts index 07029a5ff31..e2c61d20089 100644 --- a/server/src/__tests__/productivity-review-service.test.ts +++ b/server/src/__tests__/productivity-review-service.test.ts @@ -19,6 +19,7 @@ import { DEFAULT_PRODUCTIVITY_REVIEW_MAX_REFRESH_COMMENTS, DEFAULT_PRODUCTIVITY_REVIEW_NO_COMMENT_STREAK_RUNS, DEFAULT_PRODUCTIVITY_REVIEW_REFRESH_INTERVAL_MS, + PRODUCTIVITY_REVIEW_REFRESH_COMMENT_PREFIX, PRODUCTIVITY_REVIEW_ORIGIN_KIND, productivityReviewService, } from "../services/productivity-review.ts"; @@ -173,7 +174,7 @@ describeEmbeddedPostgres("productivity review service", () => { .from(issueComments) .where(and( eq(issueComments.issueId, reviewIssueId), - sql`${issueComments.body} like 'Productivity review evidence refreshed.%'`, + sql`${issueComments.body} like ${`${PRODUCTIVITY_REVIEW_REFRESH_COMMENT_PREFIX}%`}`, )) .orderBy(issueComments.createdAt); } @@ -294,6 +295,47 @@ describeEmbeddedPostgres("productivity review service", () => { expect(await listProductivityReviews(seeded.companyId)).toHaveLength(3); }); + it("does not count cancelled productivity reviews toward the creation cap", 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: DEFAULT_PRODUCTIVITY_REVIEW_NO_COMMENT_STREAK_RUNS, + now, + }); + await db.insert(issues).values( + [8, 9, 10].map((hoursAgo, index) => { + const createdAt = new Date(now.getTime() - hoursAgo * 60 * 60 * 1000); + return { + id: randomUUID(), + companyId: seeded.companyId, + title: `Cancelled productivity review ${index + 1}`, + status: "cancelled", + priority: "high", + originKind: PRODUCTIVITY_REVIEW_ORIGIN_KIND, + originId: seeded.issueId, + originFingerprint: `productivity-review:${seeded.issueId}`, + parentId: seeded.issueId, + issueNumber: index + 2, + identifier: `${seeded.issuePrefix}-${index + 2}`, + createdAt, + updatedAt: createdAt, + }; + }), + ); + + const result = await productivityReviewService(db).reconcileProductivityReviews({ + now, + companyId: seeded.companyId, + }); + + expect(result.created).toBe(1); + expect(result.creationCapped).toBe(0); + expect(await listProductivityReviews(seeded.companyId)).toHaveLength(4); + }); + it("creates a long-active review without enabling a continuation hold", 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 3ed55f734e8..1b14a1cbbee 100644 --- a/server/src/services/productivity-review.ts +++ b/server/src/services/productivity-review.ts @@ -32,7 +32,7 @@ const ACTIVE_RUN_STATUSES = ["queued", "running", "scheduled_retry"] as const; const MAX_CANDIDATE_ISSUES = 250; const MAX_RUNS_FOR_STREAK = 100; const MAX_PARENT_WALK_DEPTH = 25; -const PRODUCTIVITY_REVIEW_REFRESH_COMMENT_PREFIX = "Productivity review evidence refreshed."; +export const PRODUCTIVITY_REVIEW_REFRESH_COMMENT_PREFIX = "Productivity review evidence refreshed."; type IssueRow = typeof issues.$inferSelect; type AgentRow = typeof agents.$inferSelect; @@ -295,6 +295,7 @@ export function productivityReviewService(db: Db, deps?: { enqueueWakeup?: Enque eq(issues.originKind, PRODUCTIVITY_REVIEW_ORIGIN_KIND), eq(issues.originId, sourceIssueId), isNull(issues.hiddenAt), + sql`${issues.status} <> 'cancelled'`, sql`${issues.createdAt} >= ${cutoff.toISOString()}::timestamptz`, ), )