mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(integrations): escalate a long transient failure streak, and scope a success to its own leg
A sync failure the classifier bucketed as `transient` had no exit. The park test read `kind === "persistent"` and the persistent streak anchor was only ever stamped by a persistent failure, so a streak could run for a thousand attempts with the persistent counter at zero, the state frozen at `error_transient`, and no ladder it could climb. `transient` is a hypothesis about retryability, and the classifier defaults every timeout, 429, 5xx and unrecognised response to it precisely because the wire cannot tell a reboot from a permanent break. That default is right for one attempt and wrong for a thousand: an unbroken streak retried hourly for a full day has refuted it by experiment. Record it as `persistent` from there, which feeds the park ladder that already exists — 48 h of unbroken failure end to end before an integration stops attempting. The counters could not accumulate anyway. `IntegrationStatus` is keyed (user, integration) while Withings runs four legs on four crons and WHOOP four resources on four more, and a success cleared the error, every bucket, the streak anchor and the state whatever leg had recorded them. The ECG tick at :41 wiped what the sleep tick recorded at :15 — including a reauth park — so the strike ladder was reset several times an hour and the card painted a green "connected · 12 minutes ago" over a data class that had stopped arriving. A success now clears only what it is entitled to clear. Two columns carry it: `failing_since_at` dates the streak, `failing_leg` names its owner. The migration backfills the anchor for rows already in an error state so a connection broken for weeks escalates on its next attempt rather than restarting its clock.
This commit is contained in:
1 parent
84e38ab110
commit
527dd6c1fa
32 files changed
+823
-65
No files matched your search
@@ -110,11 +110,20 @@ const WARNING_STATES = new Set<IntegrationPillState>([
|
||||
"parked",
|
||||
]);
|
||||
|
||||
/** The states whose label carries an inline relative timestamp. */
|
||||
/**
|
||||
* The states whose label carries an inline relative timestamp.
|
||||
*
|
||||
* `warning` is in the set because leaving it out was the whole lie: the pill
|
||||
* said "sync issue" identically on hour one and on day seventeen, and the age
|
||||
* — which the mapper computes and passes in — was dropped on the floor. How
|
||||
* long it has been broken is the only part of that sentence a person can act
|
||||
* on.
|
||||
*/
|
||||
const RELATIVE_STATES = new Set<IntegrationPillState>([
|
||||
"connected",
|
||||
"stale",
|
||||
"stalled",
|
||||
"warning",
|
||||
]);
|
||||
|
||||
export function IntegrationStatusPill({
|
||||
|
||||
@@ -161,14 +161,19 @@ export function pillStateFor(
|
||||
/**
|
||||
* The timestamp the pill shows inline. `connected` reads the last success (a
|
||||
* connection row's own `lastSyncedAt` wins when it is newer, which is what
|
||||
* repaints a fresh "just now" straight after a manual sync); the two aging
|
||||
* states read the instant that triggered the verdict.
|
||||
* repaints a fresh "just now" straight after a manual sync); the aging and
|
||||
* failing states read the instant that triggered the verdict.
|
||||
*
|
||||
* `failing` must NOT fall through to the `legacyLastSyncedAt` arm below. That
|
||||
* field is a connection row's own clock, kept fresh by the very sync that is
|
||||
* erroring, so a provider that had delivered nothing for a fortnight painted a
|
||||
* "just now" beside its warning.
|
||||
*/
|
||||
export function pillTimestampFor(
|
||||
status: IntegrationStatusViewModel | undefined,
|
||||
): string | null {
|
||||
const verdict = status?.syncHealth?.verdict;
|
||||
if (verdict === "stale" || verdict === "stalled") {
|
||||
if (verdict === "stale" || verdict === "stalled" || verdict === "failing") {
|
||||
return status?.syncHealth?.since ?? null;
|
||||
}
|
||||
return status?.legacyLastSyncedAt ?? status?.lastSuccessAt ?? null;
|
||||
|
||||
Reference in new issue
Block a user