Both PICK LOCK and MASTER KEY promise the door relocks behind the
walker — not that it stands unlocked for the turn. Passing through
any door unlocked this turn now shuts it at once at the walker's
back, unless a hand holds it open; the pick-and-peek stays (an
unpassed door keeps its opening until turn's end), and holding via a
targeted cast works as ever, displayed key included. The displayed
key's own walk-through likewise leaves no lingering opening for
bystanders — their peek needs a held door. Frozen behind rev 4 so
stored games keep their turn-long openings. 18 ledgers verified; 277
tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
"Display Immediately," says the card — now the key lives up to it.
Cast bare (a new Display button beside the hold-the-door checkbox),
it goes straight on display; once displayed, it turns in every lock
its bearer walks through — no more casting at each door — with the
door relocking behind them at turn's end like any picked lock, and
the doorUnlocked event still swinging the door in the reels. Cast at
an adjacent door it still works one lock like PICK LOCK, including
holding the door open for others. A JAMmed LOCK refuses it as ever,
and the adjacent peek through workable locks already rode the rev-2
sight rules. Replay-safe ungated: bare casts were never recorded
(they were refused), displayed cards still count against the hand
limit, and walking through a locked door only widens what is
accepted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
Teleport wraps the maze's rim: its four counted squares now step
through warp mouths like any doorway, so a wizard can blink off one
side of the board and onto the other.
A wizard at a door's threshold may pull it open and peek — lock
removed, door unlocked this turn, or PICK LOCK / MASTER KEY in hand —
without stepping through. The door still hangs shut to everyone down
the hallway; only a HELD door is propped open for every eye. Gated
behind rules rev 2 (now sourced from the engine's CURRENT_RULES_REV)
so stored games replay with their doors dark; the client's sight
mirror learns the same peek.
Fear no longer nails a cornered wizard to the floor: a step closer to
the dread is allowed when it walks the shortest way out of it, so
escape can round a corner. Standing outside and stepping in is still
refused.
All 15 production ledgers verified before deploy; 269 engine tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
Blind reviews over the rev-37..40 features and the gate collapse. The
scripted surgery's scars come out: bare brace blocks unwrapped (with one
hiding an unreachable scrambleHands), the amputated comment healed, the
dead events parameter unthreaded from the illusion-sight chain, the
laziness probe in perceivedBoard collapsed to the rule it always produced,
GameView.deckRev retired with its last reader, the ward's arming stub
told honestly, and every comment or test title still citing the retired
revision numbering reworded to the rule it guards. Rim mechanics unify on
rimWarpMouth and breachRim; the test suite gains plainRimWall and
pushSustained and loses its loop-shaped scars and rev-named helpers.
No behavior changes; 255 tests pass unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 50 rooms were retired (backed up on the droplet and in Spaces), so no
ledger needs an old branch to replay: forty revisions of deckRev gates
fold into one canonical ruleset — the newest behavior everywhere. The
ward's arming, the idiot's steering, the lazy illusion roll, the legacy
redirection swap, and the flat wave all leave with their gates; RULES_REV
restarts at 1 for whenever the rules fork again. Old-vintage test pins go
with them. The opening screen now quietly drops seats whose rooms the
server no longer knows, instead of listing them "unreachable" forever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The table holds doors open to cast back through them at pursuers;
our held door opened for feet but not for eyes. From rev 15 a held
door is an open doorway to sight — casting at players, creatures,
and squares sees through it, the client's dimming aid and the
automatons alike (they share sightedCellsFor). REMOVE LOCK's "still
considered to block L.O.S." stands: that speaks of a CLOSED door,
and an unheld unlocked door still blocks. Rev-gated because rev-14
ledgers may already contain holds and must replay blind.
Pinned three ways: the holder blasts a pursuer through the held
doorway; an unheld unlocked door still blocks; rev 14 stays blind.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MASTER KEY and PICK LOCK each read: "You may 'hold the door open' for
others, if you wish" — and the engine always slammed it at end of
turn. Now the cast takes a hold param: the door stays unlocked past
the turn, for anyone, as long as its holder stands adjacent and
alive. A step away, a shove, a teleport, or a killing blow lets it
swing shut — swept after every command, since anything can move a
wizard. New state, new param: no old ledger contains either, so no
rev gate is needed.
The client offers a "hold the door open" checkbox when either card is
selected; a held door shows pale with a green jamb ("held open by a
standing wizard"), and the chronicle records the holding and the
shutting. Pinned: a held door outlives the turn and admits the other
wizard; walking away releases it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A three-reviewer sweep for tells of piecemeal machine generation,
every finding verified against the code before touching it. No
behavior changes; the full suite passes unchanged (plus two
strengthened pins).
Engine: removed four void-silenced fossils (a parseEdgeKey call
voided where it stood, stoneEffect's ignored cardId parameter, the
actualTarget remnant in doCast, a voided loop variable in shadow
upkeep); fixed the initialize-then-overwrite narration in
spawnCreature; replaced a filter(() => false) no-op; waterwall now
rides waveFromEdge instead of carrying its own verbatim copy (and the
single-caller washBack wrapper went with it); blind wall-bumps and
LOS blockers each collapsed to one implementation; the wand-id list
and the "permanent" duration sentinel became named constants; the
ambush number local no longer shadows the imported numberValue
function; assorted reviewer-aimed phrasings rewritten as the
constraints they guard.
Server/deploy: the protocol header now documents all eleven message
types; dropped an eslint pragma with no eslint, a test script with no
tests, and an rsync exclude anchored at a path that never existed
(the real data/ dir now excluded); the Caddy vhost has one source of
truth; stale "pending DNS" note removed — the record resolves.
Web: ~90 lines of CSS swallowed verbatim into a mobile media query
deduplicated; the reduced-motion guard on the board now actually
stops the marked-cell pulse; one shared color module replaces two
drifted palettes; an orphaned doc comment rejoined its function.
Tests: the ten-times-pasted helper block became test/helpers.ts;
wave-numbered files renamed for the behaviors they pin; deliberation
comments and void-ed corpses of unwritten assertions deleted; silent
seed-dependent early-returns now fail loudly; one assertion that
compared a value to itself now pins the home-translation it meant to;
the stored-log single-number command form gained the explicit
compatibility test it deserved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>