One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
v1.4.15 Phase B2, criterion 4. Every IntegrationStatus failure path
now writes a single AuditLog row with a documented schema, and this
commit lights up the real-Postgres regression guard for it:
action : "integrations.sync.failed"
userId : <subject>
details : {
integration: "withings" | "moodlog",
kind: "transient" | "reauth_required",
errorCode: string | null,
message: string, // plaintext (not encrypted —
// audit table is admin-only)
attemptNumber: number, // 1..N consecutive
state: "error_transient" | "error_reauth"
}
Successes are NOT audited — that path would be far too noisy
(every webhook-triggered sync is a row, ~ten times a day per active
user) and the integration_statuses.lastSuccessAt column already
tracks the same signal at one row per integration per user.
The integration test in tests/integration/integration-status.test.ts
provides 8 cases against a real Postgres testcontainer:
- failure writes both the integration_statuses row AND a matching
AuditLog entry, with the structured meta validated field-by-field
- lastError is encrypted at rest (column never matches plaintext)
- getIntegrationStatus() decrypts cleanly on read
- success clears the streak AND does NOT write an audit row
- admin Telegram alert fires once per burst (24h re-alert window)
- reauth_required parks the integration so isReauthRequired() is true
- markReconnected() clears the parked state without writing a fresh
success — that's the next sync's job
- markDisconnected() preserves the row as a tombstone but wipes
error state
- CASCADE delete on User removes the IntegrationStatus row
Setup file additions: integration_statuses added to the truncate-list
in tests/integration/setup.ts (already shipped under commit 4db72a8;
this commit only adds the test file that consumes it).
Tests: 11 / 39 integration pass. The dispatcher is module-mocked so
the test doesn't actually hit Telegram — we only assert that the
helper INVOKED the dispatcher with the right payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>