What changed, and why
The reveal equation showed which levers bent a turn's swing but not by how much. It named the anachronism wager (β‘) and the causal chain (β³), yet the two biggest scalers β craft and momentum β were off-screen entirely. A vague-graded Success quietly shrank to about 40% of the swing it earned (Γ0.4) with nothing on screen to account for it, so "+22 β +9%" read as random and a sharp-looking move could pay almost nothing for no visible reason (issue #184).
This change writes the numeric multiplier onto each scaler token, so the printed equation now reconciles: a reader multiplies the base by the shown factors and lands on the final. The bug case reads "+22 β vague Γ0.4 β +9%" β the 60% cut is right there. Three small moves carry it: a factor on each scaler in the one shared token builder; momentum plumbed the last mile so the ledger and codex leaf can show it too; and the ledger detail strip folded onto that same builder, retiring its own parallel badges. The blast radius is the reveal surfaces only β no scoring math moves.
The single source: a factor on each scaler token
equationTokens is the one place every surface β the thread, the codex reveal leaf, the ledger β turns a turn's record into the labeled glyphs between base and final. The change appends the multiplier to the three SCALER tokens. Craft reads its two-sided factor (the gain table on a win, the inverted loss table on a miss, so a masterful loss shows the Γ0.6 shield). Momentum shows the factor its level applies via momentumFactor, not the bare level β that's the number the equation multiplies by, and the level moves to the hover hint. Chain shows its exact carried decay factor. A short formatFactor trims each to at most two decimals so "Γ0.4" and "Γ1.45" read clean.
Each scaler appends Γfactor; formatFactor trims it. Anachronism is untouched.
utils/swing-equation.ts Β· 95 lines
β― 51 lines hidden (lines 1β51)
Momentum, carried the last mile to the ledger
Momentum already rode the thread's Message, which is why the thread could show it. The ledger event and the ripple payload never carried it, so the Spine and the codex leaf showed nothing. This adds momentumAtSwing to TimelineEvent and threads it through every carrier: the ripple frame that lands a resolved turn onto the ledger node, both addTimelineEvent sites, the snapshot and draft builders, and the two saved-run contracts. So live, end-screen, and reloaded runs disclose the same breakdown.
The new TimelineEvent field, and the ripple frame copying it onto the ledger payload.
stores/game.ts Β· 2354 lines
β― 52 lines hidden (lines 1β52)
β― 1482 lines hidden (lines 63β1544)
β― 795 lines hidden (lines 1560β2354)
The server validator that rebuilds a posted snapshot gets the matching guard. A direct POST can send anything, so momentum is coerced to a clamped integer in [0, MOMENTUM_MAX] β the same shape every other ledger field is bounded to.
Untrusted momentum clamped to the meter's range before it reaches a saved run.
server/utils/run-snapshot.ts Β· 199 lines
β― 148 lines hidden (lines 1β148)
β― 43 lines hidden (lines 157β199)
The tests reconcile the math
The unit spec pins the behavior that matters: each scaler token carries its factor, anachronism carries none, and the factors multiply out. The second test below is the issue in miniature β a chain-decayed, vague gain shows both factors, so 22 Γ 0.41 Γ 0.4 reads straight off to the result. The component specs assert the same strings render on the thread, the codex leaf, and the ledger, with data that reconciles (20 Γ 1.45 = 29, 10 Γ 1.4 = 14, β10 Γ 0.8 = β8).
Anachronism stays pip-only; every scaler factor is present and multiplies out.