TASK 55 slice 1: the startup log is size-capped

The creator (2026-10-09): 'we need some sort of log-rotation so that we don't
inadvertently fill up a user's drive.' startup.log was uncapped - 23MB and growing
every few seconds, because FfmpegEncoder logs every non-progress ffmpeg stderr line.

The policy lives in a new Helpers/LogRotation.cs as a pure function over a path
(RollIfNeeded(logPath, maxBytes, retainedFiles)), so AppLog's 156 call sites keep
their surface and the rule is testable on its own. At 5MB the live file rolls to
startup.1.log and one rolled copy is kept - the FILE SET is bounded, not just the
live file, so a long stream cannot grow the log without limit. A failed rotation
degrades to the old behaviour instead of taking the app down.

Pattern reference (cited): Serilog's file sink pairs fileSizeLimitBytes (the cap)
with retainedFileCountLimit (how many rolled files to keep) -
https://github.com/serilog/serilog-sinks-file

Guard: ytLive.Tests/AppLogRotationTests.cs - the ONE test drives the real
AppLog.Write against a temp dir (two internal seams: PathOverride, MaxBytes) and
asserts the roll, the fresh live file, and that after a SECOND roll the directory
still holds exactly startup.log + startup.1.log.

Also fixed a stale catalog row: TASK 12 (master limiter) shipped 2026-08-15 and was
still marked 'Queued'.
This commit is contained in:
2026-10-22 06:51:04 -07:00
parent 826cc17e47
commit b1d6034662
9 changed files with 201 additions and 14 deletions
+10 -1
View File
@@ -17,10 +17,19 @@ ticket and a trust breach.
**Slices (one integration test each, in order):**
1. ☐ **Size-capped log rotation** — when `startup.log` passes a cap (~5 MB), roll it to
1. ✅ **Size-capped log rotation** — when `startup.log` passes a cap (~5 MB), roll it to
`startup.1.log` (keep 1–2 old files), start fresh. Test: write past the cap, assert the roll
and the bounded total. Standard pattern (NLog/Serilog `fileSizeLimitBytes` +
`retainedFileCountLimit`) — cite in the commit.
- **SHIPPED 2026-10-22.** `Helpers/LogRotation.cs` (new) holds the policy as a pure function
over a path — `RollIfNeeded(logPath, maxBytes, retainedFiles)` shifts `name.1.log` →
`name.2.log` → … and the live file → `name.1.log`, so the **file set** is bounded, not just
the live file. `AppLog` calls it before each append (cap **5 MB**, **1** rolled copy) and
keeps its 156-call-site surface unchanged; the test seams are two `internal` properties
(`PathOverride`, `MaxBytes`) — the app project already has `InternalsVisibleTo("ytLive.Tests")`.
Guard: `ytLive.Tests/AppLogRotationTests.cs` (the ONE test: the log rolls at the cap, the
live file starts fresh, and the directory still holds exactly `startup.log` + `startup.1.log`
after a SECOND roll — the growth is bounded).
2. ☐ **Temp alert clip cleanup** — delete the `%TEMP%` alert clip on app close (and sweep
stale ones at startup). Test: create, close, assert gone.
3. ☐ **Low-disk guard for recordings** — check free space on the record folder's drive before