What changed, and why
A run can climb to real progress and then fall back. The sharpest case: the player stakes their final message, the dice crit-fail, and the staked swing doubles them all the way down to 0%. The end screen then read 0% rewritten — as if the run had never happened. The store knew the peak each turn and threw it away.
This change keeps it. The store now tracks a high-water mark as each swing lands, and the end screen shows "peaked at X%" — but only when the run actually slipped below its peak. Two source files move, both additively: the store holds the new field, the end-screen component reads it. No existing behavior changes.
The high-water mark in the store
peakProgress is a per-run number that only ever rises. It updates in applyProgress, the single place a swing lands, right after objectiveProgress is clamped into 0..100. Because it reads the already-clamped value, the mark can never exceed 100 or fall below 0 — it inherits those bounds for free.
The field, the one update site, and both resets — the whole store change.
stores/game.ts · 1463 lines
⋯ 288 lines hidden (lines 1–288)
⋯ 568 lines hidden (lines 295–862)
⋯ 330 lines hidden (lines 867–1196)
⋯ 233 lines hidden (lines 1201–1433)
⋯ 26 lines hidden (lines 1438–1463)
Surfacing it on the end screen
The component decides visibility with one computed. showPeak is true only when the peak sits above the final %. A victory always ends at 100%, which is also its own peak, so showPeak is false on every clean win. The stat appears only on a run that lost ground — which, in practice, is the defeat the feature exists for.
The conditional stat in the existing summary row, and the one-line guard.
components/EndGameScreen.vue · 309 lines
⋯ 69 lines hidden (lines 1–69)
⋯ 124 lines hidden (lines 80–203)
⋯ 101 lines hidden (lines 209–309)
Tests that pin the behavior
Two discriminating pairs, not coverage padding. In the store, the peak must survive a setback: climb to 25%, crit-fail to 0%, and the mark holds at 25% — plus both resets clear it. In the component, the stat shows when the run slipped and stays hidden on a clean 100% win.
The mark holds through a setback; resetGame clears it.
tests/unit/stores/game.spec.ts · 1492 lines
⋯ 74 lines hidden (lines 1–74)
⋯ 476 lines hidden (lines 85–560)
⋯ 926 lines hidden (lines 567–1492)
Shown on a slipped defeat (final 0%, peak 25%); absent on a clean win.