perf: cap the History list and fold column widths incrementally

History was appended to on every recorded run and never trimmed, and every
event re-sorted the whole slice and re-measured the Job, Detail and Log
columns across every row. The per-run cost therefore grew with the number of
rows, in exactly the mode the app is designed for: left in the tray for days.

The session History now keeps the newest maxHistoryRows (1000) records, the
way maxJobLogs caps a job's own activity list, and drops the oldest from the
front, zeroing the tail so a dropped record's full captured output is not
kept alive by the backing array. Column widths move into a historyLog value
that folds each new record into the current maxima instead of rescanning.
Widths only grow within a theme, so a column never narrows when a record ages
out; a theme change is the one case that still rescans, because every stored
width was measured at the old text size.

Measured with a throwaway benchmark over 5000 accumulated records: one
refresh went from 15.8 ms to 0.9 ms. At the new cap the full width rescan
alone costs 1.5 ms, so both halves of the fix carry weight.

Plan item 6 of docs/PROJECT_REVIEW_PLAN.md (finding 3.1).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 17:15:46 +03:00
parent 263717874c
commit 98c820e3bd
6 changed files with 231 additions and 30 deletions
+8
View File
@@ -65,6 +65,14 @@ change to their shape has to stay compatible on its own.
- **History tab is session-only.** `JobRuntime.Logs` exists only in memory for the
current process. Log files on disk feed aggregate statistics via `SeedStats`
only. See [ARCHITECTURE.md](ARCHITECTURE.md).
- **History is capped and its columns only widen.** The tab keeps the newest
`maxHistoryRows` records and drops the oldest, the way `maxJobLogs` caps a
job's own activity list — an app left in the tray records thousands of runs a
day, each carrying the run's full captured output. Column widths are folded in
one record at a time instead of rescanned from every row, so a column never
narrows when a record ages out: the rows on screen were laid out against the
wider value. A theme change is the one case that rescans, because every stored
width was measured at the old text size.
- Several tests share a coverage profile with another test on purpose, and a few
functions sit at 0% on purpose. Both lists live in
[TESTS.md](TESTS.md) — check them before reporting a test as redundant or a