fix: prune/retier recognize legacy rolls named off the live name; correct gzip docs

Legacy pre-v0.5.0 daily rolls were named off the LIVE file name (run.log.<date>), but
prune/retier only matched the history stem (the cwd basename), so in the default upgrade
path an existing legacy backlog was never recognized and piled up forever. prune/retier/
make_rotator/attach_rolling now thread the live name as an optional live_stem so both shapes
are pruned; a foreign same-stem file is still left alone (all forms stay date-bearing).
README + CLAUDE.md corrected to describe the atomic compress-or-skip behavior (40310b8 removed
the runtime _gz_intact reconciliation the docs still claimed).

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-06 19:35:53 -04:00
parent 3f3a797fde
commit cb8acac76f
3 changed files with 39 additions and 26 deletions
+7 -7
View File
@@ -258,13 +258,13 @@ setup_logging(name="run", queue=True)
rotate-mode aware: the zero-retention delete only ever fires for `"size"`, matching the
contract in the bullet above — `"daily"`/`"on_start"` with `backup_count=0` were always
meant to roll without pruning and now do again.
- **Gzip writes are crash-safe (v0.5.1+).** `_gzip_file` now writes to a `.tmp` sibling and
atomically `os.replace`s it onto the final `.gz` path, so a crash/OOM/power-loss mid-write
can never leave a truncated `.gz` at the path retention logic trusts. Tiered retention's
plain/gz dedupe additionally verifies a `.gz` decompresses cleanly before deleting its
plain twin — a corrupt `.gz` (from before this fix, or an external cause) is never
preferred over an intact plain copy; the plain is kept and the `.gz` gets rewritten
cleanly on the next retier pass instead of being deleted.
- **Gzip is atomic compress-or-skip (v0.5.1+).** `_gzip_file` writes to a `.tmp` sibling,
verifies it decompresses cleanly, and only then `os.replace`s it onto the final `.gz` and
removes the plain source — a crash/OOM/interrupt at any point leaves the plain `.log`
intact with no `.gz` (or the `.tmp` cleaned up), so this code never produces a truncated
`.gz` at the path retention trusts. Because a corrupt `.gz` can't arise from this path, the
tiered retention dedupe drops a plain twin unconditionally when its `.gz` exists (no
runtime `_gz_intact` reconciliation — that was removed once compression became atomic).
- **Setup-time warnings reach the log file (v0.6.0+).** Previously, a warning raised
during `setup_logging` itself (an invalid `module_levels` entry, a handler failing to
close on re-setup, an unknown `rotate` value) was emitted *before* any handler was