What changed, and why
At 0% progress, the Timeline Shift gauge told everyone the same thing: History stands unchanged…. For a fresh run, that is right. But a turn can backfire, and progress cannot fall below zero, so a −15% Critical Failure from 0% still lands right back at 0% with that same line showing. A loss looked like a no-op. The player who had just made things worse read "nothing happened".
The fix is small. When progress is 0, statusText now consults the run's ledger before it speaks. An untouched run still reads "stands unchanged". A run that already spent a turn says history pushed back instead. No new state. The transient per-turn delta chip is left alone, and only the lingering cumulative line changes.
The status line learns to read the ledger
statusText maps cumulative progress to a line of copy. The only edit sits in the p === 0 branch, which used to return a single fixed string and now chooses between two. The tell is timelineEvents, the run's record of every resolved turn. A non-empty ledger at 0% means you acted and ended up back where you started. That is a setback. It is not a blank slate.
The whole statusText map; the only behavioral change is the forked p === 0 branch.
components/ProgressTracker.vue · 204 lines
⋯ 118 lines hidden (lines 1–118)
⋯ 67 lines hidden (lines 138–204)
Why the ledger is a sound tell
Two facts in the store make the ledger reliable. First, progress is clamped at zero. That clamp is the reason a setback can pass for a fresh start. Second, every resolved turn is appended to the ledger before the gauge re-renders, and that includes the turns that net to zero or worse. So a 0% with a non-empty ledger always means a turn happened and moved you nowhere. The old copy got that case wrong.
applyProgress floors at 0 (the ambiguity); within a turn the clamped swing lands, then the turn is recorded, negative turns included.
stores/game.ts · 1455 lines
⋯ 856 lines hidden (lines 1–856)
⋯ 175 lines hidden (lines 861–1035)
⋯ 10 lines hidden (lines 1041–1050)
⋯ 389 lines hidden (lines 1067–1455)
The test
Two specs pin the behavior. The first guards the fresh case: 0% with an empty ledger still reads "stands unchanged". The second is the one only this change can pass. It records a Critical-Failure turn, leaves progress floored at 0, and checks that the line is no longer the fresh-start copy. The two states are now observably different.
Fresh vs. floored-after-setback, asserted to differ.