Skip to content

Backup covered-marker tier reader: candidate evidence

Task: 2f64e433-00e0-483e-8eb2-e106213a093c, frozen version 2. Execution: 63933884-3017-464e-8e51-029736dd5d67. Repository: h8v6/heyaira; branch: heyaira/2f64e433. Base commit: 790cc1a31f7cf5b0e2db11c969fd89ad5996050a (also local main). The candidate is the commit containing this report; its SHA is recorded in the HeyAira RESULT receipt after committing.

Cause and change

The heartbeat previously selected the greatest rolling manifest path. After retention removed a promoted rolling copy, it could miss an unchanged state still covered by a daily or monthly backup. The reader now validates complete listings of all three tiers, reads their manifests, excludes deletion-marked copies, groups by backup UUID and selects the newest timezone-aware recorded creation time. Copies of one backup must agree on creation time and marker; each manifest must name its own retained object key. Malformed metadata fails the read rather than supplying a potentially older covered marker.

Files changed:

  • src/heyaira/backup_runtime.py: tier inventory and marker selection.
  • tests/test_backup_runtime.py: synthetic bucket regressions and fixture metadata.
  • docs/BACKUP_RUNBOOK.md: heartbeat tier-selection sentence.
  • This report: local verification evidence.

Verification

Tests use synthetic manifests and command runners; no real bucket is contacted. Commands run in the repository root with TMPDIR="$PWD/.local-test-tmp", which keeps temporary files inside the authorized workspace.

Before the runtime fix, the initial expanded reader tests produced:

18 failed, 8 passed in 0.61s

This reproduced missing daily/monthly coverage, wrong cross-tier selection, deletion-marked coverage and accepted incomplete/malformed promoted-tier listings. The failures also included the large-listing test's new expectation that every manifest is inspected to compare recorded creation times.

After the fix and additional invalid-metadata regressions:

$ PYTHONPATH=src python -m pytest -q -p no:cacheprovider tests/test_backup_runtime.py
....................................                                     [100%]
36 passed in 4.74s

Full suite, with the existing Xcode Git binary prepended to PATH to avoid the macOS /usr/bin/git wrapper's repeated denied cache writes:

$ PYTHONPATH=src python -m pytest -q -p no:cacheprovider
FAILED tests/test_server_beat_audit.py::test_R6_02_tcp_connect_gets_only_the_budget_left_after_resolution
1 failed, 417 passed, 232 skipped in 36.93s

The unchanged SMTP test expects a synthetic connect callback within a 0.2-second deadline and total elapsed time below 0.3 seconds. An earlier full run failed because that callback was never reached:

tests/test_server_beat_audit.py:1092: AssertionError
1 failed, 417 passed, 232 skipped in 62.23s (0:01:02)

Its isolated retry reached the callback but exceeded the elapsed-time bound:

$ PYTHONPATH=src python -m pytest -q -p no:cacheprovider tests/test_server_beat_audit.py::test_R6_02_tcp_connect_gets_only_the_budget_left_after_resolution
E       assert (122449.847424583 - 122449.521191625) < 0.3
tests/test_server_beat_audit.py:1093: AssertionError
1 failed in 0.84s

The final full run again failed because the connect callback was never reached. Neither this test nor SMTP code was changed; the suite criterion is therefore reported as failed rather than masking an unrelated timing-sensitive failure. The first run through the slow macOS Git wrapper was interrupted after 192 passed, 31 skipped in 728.48s (0:12:08); it is not a successful suite run.

git diff --check passed.

Acceptance criteria

  • tiers-read: daily-only and monthly-only retained backups are found; each tier can win by recorded creation time, including differing UTC offsets; duplicate backup copies contribute one identity. Conflicting copies raise.
  • deleting-ignored: a newest deletion-marked copy in each tier falls back to the older valid backup; an unmarked duplicate remains eligible; only marked copies produce no covered marker.
  • listing-guards: the existing rolling truncation test bodies remain unchanged and pass, including the real bounded-subprocess rejection. Daily/monthly truncation and missing-final-newline regressions pass, as do malformed path regressions; invalid listings cause no manifest downloads.
  • suite: fail. The full database-free suite has one unchanged SMTP deadline-test failure; 417 tests passed and 232 were skipped using the repository's existing database and listening-socket skip conditions.

Limits and handoff

Confidence in the reader change is based on its passing local synthetic regressions; the overall suite is not green. Database-backed and listening-socket tests are subject to the sandbox's existing skip conditions and remain for CI. No production bucket, database, push, pull request, merge, installation or deployment was used.

Comparing recorded times requires reading all listed manifests per poll; the existing manifest poll cache remains in place. Actual R2 latency and independent workflow acceptance have not been measured or granted here. The next step is independent verification of this local candidate commit.