Files
ServerKit/backend
Juan DenisandClaude Opus 5 ff2aef1d44 fix(backups): a skipped restore drill no longer reads as proof
plan 75 §A1 — and it was a live bug, not the hardening pass I expected.

_drill_badge() returned 'ok' for any status that was not literally 'failed'. A
drill skipped for lack of scratch space records 'skipped_no_space' AND stamps
last_drill_at, so it fell through every branch and Doctor reported:

    "A recent restore drill proved this backup restores."

about a drill that never executed. It also self-perpetuated: each skip
refreshed last_drill_at, so a policy permanently short of scratch space could
never even go stale. It would report proven forever, having proven nothing —
and "short of scratch space" is the normal state of a full disk, which is
exactly when you most need the backup.

The drill service was careful (its own comment reads "loud skip, never a silent
pass") and the frontend renders an amber pill. The honesty was lost in the one
function that summarises it, which is the one Doctor reads.

Fixes:
- 'ok' must now be positively earned: only last_drill_status == 'success'
  reaches it. Adds 'skipped' and 'unknown' states, so an unrecognised future
  status cannot reintroduce this by falling through.
- staleness is evaluated only for a drill that actually succeeded, since it is
  a property of proof rather than of attempts.
- Doctor renders both new states as warn with what was and was not proven.
- the skip path now notifies. BackupAlertService.on_drill_result already mapped
  skipped_no_space to a failed outcome, but only the exception path ever called
  it, so that branch was unreachable and a skipped drill told nobody. A log
  line is not a notification.

Mutation-checked: with the fix reverted, the 5 bug-targeting tests fail and the
9 pinning existing behaviour still pass. 307 green across backup/doctor/restore
(a 422 in test_raw_infra_authz did not reproduce on re-run and is absent from
the baseline selection — the documented intermittent, not this change).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 21:03:06 -04:00
..