A per-player NPC rival who "runs" the same zone progression on a midnight
ticker at ~1.3x the player's own clear pace, staying just ahead so it's a
race you can always see and nearly catch. Pure theatre: no combat, no
punishment, only race pressure and two payoffs at each zone clear.
- New adventure_shadow table, deliberately OUTSIDE the player_meta save
fan-out so a character save can never clobber the ticker's advance (the
isolation journal_pages earns by being grant-only, made structural). No
bootstrap: absent row == no Shadow, minted lazily on first advance.
- midnightReset advances every player's Shadow once per UTC day (own
idempotency guard); lead-capped so it never runs >2.5 zones ahead. When it
clears a zone the player hasn't, it leaves a journal page waiting (D1 tie-in).
- Morning-briefing race-pressure one-liners (TwinBee voice, deterministic).
- Zone-clear payoff in finalizeExpeditionOnZoneClear: a bonus-XP crow when the
player got there first, or the Shadow's waiting page when it did.
- !adventure shadow status view.
Review fixes (3 finders + verify) folded in before commit:
- Crow XP is now set-once per zone (crowed_mask), so re-running a zone the
Shadow hasn't reached can't farm it.
- The waiting page is granted BEFORE the pending bit is retired, so a transient
grant failure leaves the debt for the next clear instead of swallowing a page.
- The crow line no longer claims "+XP" when the grant errored.
Combat golden byte-identical (Shadow never touches SimulateCombat); go
build/vet/test green repo-wide.
Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa