From 131e83ce77249dc10afda09d157bb48ca33922a8 Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Wed, 5 Aug 2026 01:00:07 +0800 Subject: [PATCH] fix(bom-costing): reset recovery budget after progress --- .../repositories/bom-costing.repository.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/furniture-3d-system/src/server/repositories/bom-costing.repository.ts b/furniture-3d-system/src/server/repositories/bom-costing.repository.ts index 0b10f093..555bd1bb 100644 --- a/furniture-3d-system/src/server/repositories/bom-costing.repository.ts +++ b/furniture-3d-system/src/server/repositories/bom-costing.repository.ts @@ -843,12 +843,27 @@ export class BomCostingRepository { }) if (!run) return false - // Count any automatic recovery for this case. A finite limit prevents a - // persistent provider outage from making an invisible endless loop. + // Only count recoveries after the most recent successful synthesis batch. + // A successful batch proves the provider recovered, so older retries must + // not consume the budget for a later, unrelated short-lived timeout. + const latestSynthesisCompletion = await tx.bomCostingTaskEvent.findFirst({ + where: { + caseId: input.caseId, + eventType: 'MODEL_SYNTHESIS_BATCH_COMPLETED', + }, + orderBy: { createdAt: 'desc' }, + select: { createdAt: true }, + }) + + // Keep a finite limit for consecutive failures so a persistent provider + // outage still cannot make an invisible endless loop. const recoveryCount = await tx.bomCostingTaskEvent.count({ where: { caseId: input.caseId, eventType: 'TASK_RECOVERY_QUEUED', + ...(latestSynthesisCompletion + ? { createdAt: { gt: latestSynthesisCompletion.createdAt } } + : {}), }, }) if (recoveryCount >= maxAutoRecoveries) return false