From f1e16bf713de6b8f5663617ef1201066614612f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 23:05:59 +0200 Subject: [PATCH] fix(integrations): increment per-kind bucket on back-fill path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The legacy single-counter column writes { increment: 1 } so the post-write value lands at N+1, but the back-fill seed wrote buckets[kind] = legacyCount (= N) without a compensating +1. Post-write the bucket would read N while the legacy column read N+1; when v1.4.44 drops the legacy column every back-filled row would page exactly one failure too late. v1.4.43 W10 medium-reconcile — senior-dev M-1. --- src/lib/integrations/__tests__/status.test.ts | 7 +++++-- src/lib/integrations/status.ts | 16 +++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/lib/integrations/__tests__/status.test.ts b/src/lib/integrations/__tests__/status.test.ts index 8315a1826..3d91d6d0e 100644 --- a/src/lib/integrations/__tests__/status.test.ts +++ b/src/lib/integrations/__tests__/status.test.ts @@ -662,10 +662,13 @@ describe("recordSyncFailure — v1.4.43 W14 per-kind buckets", () => { // The 3 legacy failures get bucketed into the CURRENT failure's // kind (reauth_required) — that's the kind the row was last // tagged with, and back-filling into a different kind would - // under-count the existing streak. + // under-count the existing streak. v1.4.43 W10 senior-dev M-1 — + // the back-fill branch additionally increments the bucket so the + // post-write bucket count matches the legacy column's post-write + // value (legacy: 3 → 4, bucket: 0 → 3 → 4). expect(upsertArgs.update.consecutiveFailuresByKind).toEqual({ transient: 0, - reauth_required: 3, + reauth_required: 4, persistent: 0, }); }); diff --git a/src/lib/integrations/status.ts b/src/lib/integrations/status.ts index 1a567a086..ae9fb9cb8 100644 --- a/src/lib/integrations/status.ts +++ b/src/lib/integrations/status.ts @@ -381,18 +381,24 @@ export async function recordSyncFailure( // Increment only the bucket matching this failure's kind. // The other two buckets stay at their current value so a persistent // streak isn't reset by an intervening transient hiccup. + // + // v1.4.43 W10 senior-dev M-1 — back-fill branch also increments. The + // legacy column writes `{ increment: 1 }` to advance from N → N+1; the + // back-fill seed sets `buckets[kind] = legacyCount` (= N), so without + // the +1 here the bucket would land at N while the legacy column reads + // N+1. When v1.4.44 drops the legacy column, every back-filled row + // would page exactly one failure too late. if (startingBuckets) { // Post-migration row — increment the actual bucket. buckets[kind] = (buckets[kind] ?? 0) + 1; } else if (!existing) { // First-ever write — bucket starts at zero, this failure makes 1. buckets[kind] = 1; + } else { + // Back-fill path — seeded with the legacy count; this failure is + // the +1 that brings both the legacy column AND the bucket to N+1. + buckets[kind] = (buckets[kind] ?? 0) + 1; } - // else: back-fill path already seeded `buckets[kind]` with the legacy - // count; the current failure IS counted in that seed so no second - // increment is needed (a row at 4 failures sitting in error_reauth - // back-fills to reauth_required: 4 and this 5th failure makes the - // legacy +1 path bring the bucket to 5 below). // Track the persistent-streak start so the >24h park check has a // wall-clock anchor. Only stamped on the FIRST persistent failure of