From 90f36ac9b243ed4dad22141a26a94bf2caccfcec Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Feb 2026 09:29:57 +0100 Subject: [PATCH] Retroactive auto-PASS for projects with complete documents Wire batchCheckRequirementsAndTransition into round activation and reopen so pre-existing projects that already have all required docs get auto- passed. Also adds checkDocumentCompletion endpoint for manual sweeps on already-active rounds. Co-Authored-By: Claude Opus 4.6 --- src/server/routers/roundEngine.ts | 37 +++++++++++++++++++++++++++++ src/server/services/round-engine.ts | 35 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/server/routers/roundEngine.ts b/src/server/routers/roundEngine.ts index 5851cf2..5016bcc 100644 --- a/src/server/routers/roundEngine.ts +++ b/src/server/routers/roundEngine.ts @@ -263,4 +263,41 @@ export const roundEngineRouter = router({ return { success: true, removedCount: deleted.count } }), + + /** + * Retroactive document check: auto-PASS any PENDING/IN_PROGRESS projects + * that already have all required documents uploaded for this round. + * Useful for rounds activated before the auto-transition feature was deployed. + */ + checkDocumentCompletion: adminProcedure + .input(z.object({ roundId: z.string() })) + .mutation(async ({ ctx, input }) => { + const { batchCheckRequirementsAndTransition } = await import('../services/round-engine') + + const projectStates = await ctx.prisma.projectRoundState.findMany({ + where: { + roundId: input.roundId, + state: { in: ['PENDING', 'IN_PROGRESS'] }, + }, + select: { projectId: true }, + }) + + if (projectStates.length === 0) { + return { transitionedCount: 0, checkedCount: 0, projectIds: [] } + } + + const projectIds = projectStates.map((ps: { projectId: string }) => ps.projectId) + const result = await batchCheckRequirementsAndTransition( + input.roundId, + projectIds, + ctx.user.id, + ctx.prisma, + ) + + return { + transitionedCount: result.transitionedCount, + checkedCount: projectIds.length, + projectIds: result.projectIds, + } + }), }) diff --git a/src/server/services/round-engine.ts b/src/server/services/round-engine.ts index 4d0c126..acb2efd 100644 --- a/src/server/services/round-engine.ts +++ b/src/server/services/round-engine.ts @@ -143,6 +143,24 @@ export async function activateRound( detailsJson: { name: round.name, roundType: round.roundType }, }) + // Retroactive check: auto-PASS any projects that already have all required docs uploaded + // Non-fatal — runs after activation so it never blocks the transition + try { + const projectStates = await prisma.projectRoundState.findMany({ + where: { roundId, state: { in: ['PENDING', 'IN_PROGRESS'] } }, + select: { projectId: true }, + }) + if (projectStates.length > 0) { + const projectIds = projectStates.map((ps: { projectId: string }) => ps.projectId) + const result = await batchCheckRequirementsAndTransition(roundId, projectIds, actorId, prisma) + if (result.transitionedCount > 0) { + console.log(`[RoundEngine] On activation: auto-passed ${result.transitionedCount} projects with complete documents`) + } + } + } catch (retroError) { + console.error('[RoundEngine] Retroactive document check failed (non-fatal):', retroError) + } + return { success: true, round: { id: updated.id, status: updated.status }, @@ -429,6 +447,23 @@ export async function reopenRound( }, }) + // Retroactive check: auto-PASS any projects that already have all required docs + try { + const projectStates = await prisma.projectRoundState.findMany({ + where: { roundId, state: { in: ['PENDING', 'IN_PROGRESS'] } }, + select: { projectId: true }, + }) + if (projectStates.length > 0) { + const projectIds = projectStates.map((ps: { projectId: string }) => ps.projectId) + const batchResult = await batchCheckRequirementsAndTransition(roundId, projectIds, actorId, prisma) + if (batchResult.transitionedCount > 0) { + console.log(`[RoundEngine] On reopen: auto-passed ${batchResult.transitionedCount} projects with complete documents`) + } + } + } catch (retroError) { + console.error('[RoundEngine] Retroactive document check on reopen failed (non-fatal):', retroError) + } + return { success: true, round: { id: result.updated.id, status: result.updated.status },