feat(progression): Playthrough aggregate (slice 3)

no LoseLife (ADR-0005). SettleDeath / SettleCompletion defer to the
Attempt's terminal outcome, so both are atomic and idempotent.
SettleCompletion branches on HasCleared itself - a Replay cannot bank
or move campaign position backwards (ADR-0003).

Campaign value object holds authored level order; ReachableLevels is
derived, replacing the stored UnlockedLevels. Coins.IsAtLeast driven
out by cross-scope affordability.

29 tests. CONTEXT.md: Campaign, Settle.
This commit is contained in:
2026-07-28 18:52:20 +02:00
parent 40bd5c1d86
commit ffa2b986ae
7 changed files with 370 additions and 2 deletions

View File

@@ -18,6 +18,8 @@ public readonly struct Coins : IEquatable<Coins>
public Coins Add(Coins other) => new(Value + other.Value);
public bool IsAtLeast(Coins other) => Value >= other.Value;
/// <summary>
/// Subtracts <paramref name="amount"/>. Returns false when the amount exceeds what is held,
/// leaving <paramref name="remaining"/> untouched rather than clamping to zero.