Completes the milestone feature that v0.8.1 partially shipped. Stack-based
reward systems (Bennies, Fate Points, Hero Points) need to know how many
the player has — the feed block alone wasn't enough. Sidebar now shows
"BENNIE 3" / "HERO POINT 2" pills per character.
What's new
- Milestone counter in player sidebar. One row per active label with a
gold count pill. Labels with count 0 don't render (and are removed
from the underlying milestones dict on decrement-to-zero) so the card
stays clean.
- Server mutation ops _milestone_inc / _milestone_dec follow the same
pattern as _conditions_add / _slot_use. Increments respect optional
milestone_caps per label (set to {Inspiration: 1} for D&D 5e binary).
Decrements floor-clamp at 0.
- send.py now POSTs to both /chunk and /stats for milestone events.
/chunk renders the gold-glow feed block (already shipped in v0.8.1);
/stats drives the new sidebar counter.
Tests (63 total, up from 55)
- 8 new MilestoneCounterTests covering increment, decrement, cap
enforcement, label removal at zero, multi-label coexistence, and
cross-mutation isolation.
Demo verification
- 3 awards + 1 spend on Aldric → {Bennie: 2, Hero Point: 1}.
- Mira with milestone_caps {Inspiration: 1} correctly stays at 1 after
two award calls.
Compatibility
- Existing players without a milestones field get one created on first
award. No migration needed.
- send.py back-compat: --milestone-award and --milestone-spend now also
fire stats POSTs, but existing usage that didn't expect a sidebar
counter still works (the counter just appears).