From 8579705fbf3b52723048d12d00024dedb310b03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 5 Jul 2026 13:27:30 +0200 Subject: [PATCH] fix(insights): keep the briefing on a transport-failed grounding retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the corrective grounding retry dies on transport (provider outage, timeout) the model never got its chance to self-correct — stripping the briefing to null left the morning surface empty. The strip now falls back to the previous payload's briefing, which passed this same gate when it was written; a content-failed retry still strips hard so a fabricated number never persists. --- src/lib/insights/comprehensive-generate.ts | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/lib/insights/comprehensive-generate.ts b/src/lib/insights/comprehensive-generate.ts index be4ab8ca4..cef38114d 100644 --- a/src/lib/insights/comprehensive-generate.ts +++ b/src/lib/insights/comprehensive-generate.ts @@ -1111,6 +1111,7 @@ export async function generateComprehensiveInsight( // the briefing rather than persist a fabricated number. { const signals = features.signalsOfDay ?? null; + let retryTransportFailed = false; let ungrounded = findUngroundedBriefingNumbers( readBriefingBlock(insights), signals, @@ -1151,14 +1152,39 @@ export async function generateComprehensiveInsight( ungrounded = retryUngrounded; } } catch { - // Retry failed — fall through to the strip below. + // Retry failed on TRANSPORT (provider outage / timeout), not on + // content — the model never got its correction chance. + retryTransportFailed = true; } } if (ungrounded.length > 0 && insights && typeof insights === "object") { - (insights as Record).dailyBriefing = null; + // A content-failed retry (the model repeated ungrounded numbers) + // still strips hard — a fabricated figure must never persist. But + // when the corrective retry died on transport, prefer the previous + // payload's briefing (it passed this same gate when it was written) + // over a hole: a day-old briefing beats a vanished one, and the next + // successful run replaces it. + let fallbackBriefing: unknown = null; + if (retryTransportFailed && dbUser?.insightsCachedText) { + try { + const prev = JSON.parse(dbUser.insightsCachedText) as Record< + string, + unknown + >; + fallbackBriefing = prev.dailyBriefing ?? null; + } catch { + // Unparseable previous payload — keep the hard strip. + } + } + (insights as Record).dailyBriefing = fallbackBriefing; annotate({ action: { name: "insights.generate.briefing_grounding_stripped" }, - meta: { locale, ungroundedCount: ungrounded.length }, + meta: { + locale, + ungroundedCount: ungrounded.length, + briefing_fallback: + fallbackBriefing != null ? "previous-cached" : "stripped", + }, }); } }