Commit Graph
336 Commits
Author SHA1 Message Date
Eric WagonerandClaude Fable 5 6c48174376 Screenplays: authored scenes the engine performs and the reel films
?fpv&script=<name> plays a screenplay — a seeded deal, cards slipped
into hands as props, then a command list run through applyCommand, so
every event on screen is a legal move under the real rules. Ten scenes
ship in the catalog: wormhole heists, a fireball threaded through a
warp, an ambush that watches its corridor through a mouth, the leap
over a conjured pit, and the wall between two homes coming down.

The server grows a public gallery to hang them in: /clips lists the
catalog, /clips/<name> pages each scene in two takes — through the
wizard's eyes and from the board — with og:video unfurls and
Range-served mp4s (Safari refuses video without it). Files live in
the clip vault beside the rooms, shipped by deploy/clips-publish.sh;
the name gate is the security.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 13:16:01 -04:00
Eric WagonerandClaude Fable 5 7c201554f5 The cutaway camera may not walk through a warp mouth
A ray threads a warp and keeps counting; a body cannot. Both cutaway
cameras (reel and live pane) backed down deepestFacing's corridor,
which meant a mouth-adjacent shot could measure its backing room
THROUGH the warp and park the eye off the board — a second of smeared
nothing whenever someone stepped a rim warp. cutawayStand now measures
standable ground, clamping warped rays at the mouth, so the shot stands
on real floor watching the veil instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 12:41:17 -04:00
Eric WagonerandClaude Fable 5 c8f719f467 The cockpit's webs and dread ring learn to sit right
Sticky-web silk becomes a real web — hub, spokes, two sagging rings —
each strand shadowed so it reads against busy token art. The dread
ring moves behind the body onto the floor plane, sharing the ember
ring's flattened perspective instead of smouldering over the sprite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 12:17:55 -04:00
Eric WagonerandClaude Fable 5 19888f9e84 FEAR stains the forbidden ground in the cockpit too
The aura's warp-walking diamond — dreadDistance ≤ 3, extracted to one
shared fearCells so the board's dashed ring, the cockpit's floor stain,
and the engine's refusal can never disagree — now tints the flagstones
violet in first person, breathing like the board's aura, beneath the
dread ring already at the bearer's feet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 12:06:00 -04:00
Eric WagonerandClaude Fable 5 203b6101bd The cockpit wears the board's ongoing spells
Sticky webs strand across a caught body, MEDUSA drains it to gray
stone, the invisible and the misted go translucent, BIG MAN towers and
SHRINK dwindles the billboard, and FEAR smoulders a dark-red dread
ring at its bearer's feet — the six condition looks the 2D board wore
alone. Marks flow billboard → projection → draw, painted only when
the walls show the sprite. The workshop poses them: ?wear=card;card
dresses the Rival.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 12:00:28 -04:00
Eric WagonerandClaude Fable 5 b996c2bc93 The cockpit learns to aim: marked beings, verdict-bearing crosshair
While a targeted card is armed, wizards and creatures standing on
castable ground wear a pulsing ember ring at their feet — drawn behind
the body, only when the walls actually show it, and only when the card
could land on a being (creations still light floor alone). The hover
cue now carries a verdict: gold outline and bright name for what the
cast can reach, cold slate for what stands beyond it. The workshop
poses it all: ?aim=1 arms a pretend cast and ?rival=x,y stands the
Rival wherever the shot needs them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 11:41:27 -04:00
Eric WagonerandClaude Fable 5 afa0e17483 Credibility pass: the fast-edit shavings swept from the ops-and-rev-13 batch
Server: turnFacts joins actingSeat's full priority order (a hanging
ward or slow-death window now reads as the lobby's attention, stubs
included), summarize and stubOf share one baseSummary, the stranded
doc comment returns to summarize, the protocol contract names
feedbackList, Sentry init moves below the imports it cannot precede,
randomBytes joins its group, the feedback path hoists once with the
operator-fields note. Engine: the safe's sight check trusts castSight
alone, the locked-safe predicate becomes lockedSafeAt, the doomed-grab
test drives both seats and must reach endTurn. Web: the twin safe
handlers merge, paPoints joins the reset ritual with a derived burnable
range, five decorative empty handlers and a dead class go, the hand
earns real list semantics, fitHandCards cites Card.svelte's tokens,
two wrap-sites reindent, feedbackReports moves home as a named type,
and the chronicle's stay-mounted reason is stated. feedback-reply.sh
adopts its siblings' set/usage conventions and speaks Node. Two test
screenshots leave the repo root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 11:04:01 -04:00
Eric WagonerandClaude Fable 5 8cfd911a3e Slow Death's draw is one blow, countered against its total (table ruling)
Two cards drawn is a two-point blow: ABSORB soaks up to three of the
total, BLUNT halves it rounding up (so it finally earns a place — 2
becomes 1), counters may chain, declining takes the rest. The client
labels each counter with the exact points it leaves; the bots weigh
the total against their floor. Amends rev 13 minutes after it shipped;
every production ledger still replays clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 10:41:47 -04:00
Eric WagonerandClaude Fable 5 92613d7386 Ops hardening plus two rules gaps close (rev 13)
Operational: auto-reboot for kernel patches (09:30 UTC), Caddy access
logs at /var/lib/caddy (self-rotating; the sandbox denies /var/log),
logrotate for the backup log, a restore drill proving a Spaces snapshot
boots 56/56 rooms clean, the about page's plain sentence on permanent
recording, and /wizwar-pulse to read it all weekly.

Rules: safes now smash — attacks aimed at the box accumulate on it and
the fifteenth point bursts it (widening, ungated). And SLOW DEATH's
bites pause for a victim holding an ABSORB — the card named by the FAQ,
not Absorb Spell — each soaking exactly one point, rev-gated at 13 with
the instant-bite legacy pinned. Bots weigh the soak against their life.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 10:35:52 -04:00
Eric WagonerandClaude Fable 5 a66b058c68 The wizwar Sentry project goes live: DSN into service and bundle
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 10:01:02 -04:00
Eric WagonerandClaude Fable 5 b06da1f1f1 Sentry plumbing, inert until a DSN exists
Server: errors-only @sentry/node behind SENTRY_DSN, capturing the
unhandled-protocol catch with its message type and room. Client:
errors-only @sentry/browser behind VITE_SENTRY_DSN baked at build.
Backup: Sentry Crons check-ins (in_progress/ok/error) when
/root/.wizwar-sentry-cron holds the check-in URL. No DSN, no behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:58:26 -04:00
Eric WagonerandClaude Fable 5 539c2f25bb The reports ledger loads on connect, not into a closed socket
The first refreshFeedback fired while the websocket was still
connecting and pre-open sends drop silently; the games list survives
because onopen re-asks. Feedback now re-asks there too, and rides the
45-second poll so replies land while the lobby sits open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:37:46 -04:00
Eric WagonerandClaude Fable 5 5abd0ba60e Players see their reports answered in the lobby ledger
Reports gain ids; replies live in the same JSONL, folding onto their
report when read (legacy reports answer to their timestamp). The lobby
asks with the same seat-token proof as the games ledger — wake-free —
and lists each report with its reply and status, or 'the wizards are
studying the moment'. deploy/feedback-reply.sh answers one from here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:34:20 -04:00
Eric WagonerandClaude Fable 5 eabc10f18c Safes finally open to keys — and the clockwork stops clawing at the lid
'All LOCK-type cards will work on it' was never wired: openSafes was
reset every turn and filled by nothing. A PICK LOCK or MASTER KEY
aimed at a safe's square (underfoot or beside) now opens it until
turn's end, with its own chronicle line; the dead never-emitted
safeOpened variant and its null humanize go. The automaton — H4EN's
Automaton II spent six turns grabbing at a locked lid saying 'I meant
to do that' — now cracks the box first (key or DISPEL CREATION, which
it held the whole time) and drops unopenable safes from its goals
rather than marching to one forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:22:56 -04:00
Eric WagonerandClaude Fable 5 12ece6065a Mad Dash doubles everything it promises (rules rev 12)
'Including NUMBER cards and other add-ons' now means it: a number
riding the cast fuels the dash — (base + number) × 2 — and numbers or
POWER RUN points played under it double as well. The 5BM4 five that
vanished into a borrowed 'burns 0 life for speed' line gets its own
madDash event and chronicle voice. Older games doubled only the base
and replay so, pinned at deckRev 11.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 16:25:30 -04:00
Eric WagonerandClaude Fable 5 6189b4935c The casting panel: the rail becomes the spell while you build it
While a card is selected (or an attack demands an answer) on a wide
screen, the chronicle and chat yield their column to a casting panel:
the card at reading size, attached numbers and modifiers as cards with
their total, and the whole guidance strip — targeting hints, pickers,
discard, cancel — moved in beside them. Counteractions get the same
panel with the incoming card, its power, and every counter played so
far. The under-board strip remains for phones, where a rail below the
fold is no place to aim from. The guidance markup extracts into one
snippet rendered wherever the mode calls it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 16:12:12 -04:00
Eric WagonerandClaude Fable 5 beaae4a0a4 The selected-card preview zooms to the full reading modal on a click
Selecting a hand card must not throw a modal over the board — the aim
comes next — so the corner preview becomes the doorway: click it and
the card opens at tile-peek size, close it and the selection stands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 15:55:21 -04:00
Eric WagonerandClaude Fable 5 51aae53ae2 Player yellow brightens so laptop panels cannot read it as orange
Mustard #c9a72a drifted orange on washed-out screens and blurred into
the red beside it in rings, dots, and names. #ecc716 stays yellow on
any panel. The accent gold elsewhere keeps the house tone — it never
stands next to a player's red.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 15:50:42 -04:00
Eric WagonerandClaude Fable 5 9aaccf4f30 Six answers to the first wave of player feedback
The turn slip says 'turn spent' instead of counting phantom moves once
actions end; the attacking card (or the counter being countered) shows
in the respond slip so counters are chosen with eyes open; the absorb
chronicle line names the card it swallowed — which answers the KTXQ
mystery, where an automaton's ABSORB SPELL took Blind into its hand and
Reuse Spell rightly found nothing to retrieve. POWER ATTACK gains its
missing UI: attach it like any modifier and pick the life to burn.
Hand cards drag to reorder (browser-local arrangement over the
server's authoritative contents), and the left-hand layout scales its
two columns to the space without ever growing a scrollbar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 15:47:37 -04:00
Eric WagonerandClaude Fable 5 9aac790e71 Four token revisions clear the board's last confusions
The Democratic Monster goes crab-red (two heads, per the card), the
rock warms to brown against the gray stone, and the ooze and slime
split on silhouette — one stands up toothy, the other hangs and drips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 14:52:36 -04:00
Eric WagonerandClaude Fable 5 553754a596 Mid-game invite links and a surprise-report slip
The masthead's room code is now a button: one click copies the /join
link, which seats newcomers in a waiting room and the gallery once the
boards flip. And a 🐞 report opens a two-question slip — what happened,
what was expected — that lands in feedback.jsonl beside the rooms with
the room code, move number, round, and rules rev attached, so any
surprise can be replayed to its exact moment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 14:08:39 -04:00
Eric WagonerandClaude Fable 5 a1fdb37c54 Credibility pass: the splice marks sanded from the rev-11 batch
The extracted rebuildRoom loop reflowed to the file's indent and its
doc comments unstacked; liftIdiotIfSatisfied moves below idiotBlocked
so each keeps its own comment; the pickup's duplicated owner lookup
folds to one; the cloned grab-refusal chain hoists to grabRefused; a
one-off #8a7a5c rejoins the #8a7a5e palette; an inert overflow-x line
and a duplicate import go; span 6 and the two cache strategies say why;
the reversed walking-dead test moves beside its biting sibling. Also:
the replay harness now honors lobby kicks, which VVZU (five bots
seated, two kicked) was the first production ledger to exercise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 13:58:02 -04:00
Eric WagonerandClaude Fable 5 9cc0430ee2 The left-hand hand deals into two columns and the map takes the height
Player refinement: no vertical scrolling through a tall single file —
the side hand lays out two cards abreast, and with the bottom strip
holding only buttons the board viewport stretches to the freed room.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 23:36:13 -04:00
Eric WagonerandClaude Fable 5 d334b267a2 The hand may stand at the board's left (preference)
A player asked for cards down the side instead of along the bottom.
With the new pref on (wide screens only), the hand claims a sticky,
scrolling column at the left while the hint strips and action buttons
stay beneath the board — the table-edge dissolves into the grid so its
children place themselves. The strip moves inside the .game grid to
make that possible; the default bottom layout spans the full row as
before, and phones keep their one-column flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 23:28:00 -04:00
Eric WagonerandClaude Fable 5 4430a0def1 The board gets a zoom dial, and its size is never left to chance
Players wanted to magnify crowded squares; the board now lives in a
scrolling viewport with +/− controls (1x fit to 4x), zoom centered on
the view. The svg's width is always set explicitly — fit × zoom,
remeasured by a ResizeObserver — which also cures the stale small
board left behind when the first-person pane closed: auto-sizing
against shifting CSS constraints is gone entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 22:57:55 -04:00
Eric WagonerandClaude Fable 5 7f82aa47e0 Auto-grab asks before pocketing your own treasure
Walking off a square holding your own chest is the capture clause's
favorite setup, so the end-turn caution now names it before the
auto-grab preference takes it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 22:36:04 -04:00
Eric WagonerandClaude Fable 5 691f44d858 Room invite links: /join/<CODE> recruits with a card of its own
A living room's link unfurls as an invitation (waiting rooms beckon a
seat, started games a gallery view) via the shared ogPage baker. The
client follows the path: a held seat resumes, a waiting room offers
join-or-watch from the lobby, a started game is watched at once. The
address bar carries the invite link at any table, with a copy button
beside the roster.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 21:57:38 -04:00
Eric WagonerandClaude Fable 5 e93bcc15a2 Credibility pass: session residue swept from the rev-10 run
War-story comments become timeless constraints (carrier rule, boobytrap
tell, remove-curse prescription); the rev changelog reunites under
GameConfig.deckRev; three hand-copied cardless stack literals fold into
openCardlessStack; the visionstone one-edge loop is shared via
throughOneEdge with the edge-target hairpin exclusion stated; drain()
moves to test helpers and replaces four inline copies; the butt-head
sweep test loses its dead scaffolding and its expect(refused || true)
tautology and now asserts both branches; a process-named suite is
renamed for the behavior it pins; dead .bezel-spacer CSS and the fx
effect's fossilized cleanup story go. No behavior changes: 304 engine
tests pass, 36/36 ledgers replay, web typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 19:14:03 -04:00
Eric WagonerandClaude Fable 5 b99c22f14a Disease makes its caster the carrier (rules rev 7)
The card says it plainly — "You're the carrier! Disease caused by
spell does not affect you, only others" — but the engine cast it AT an
adjacent victim, making them the plague rat. At rev 7 it is a
self-cast on fear's pattern: no target, no counteraction window
("REFLECTIONs have no effect against this"), duration equals the
number, and sharing a square bites in both directions — the carrier
walking in, or anyone walking onto the carrier. Older ledgers hold
targeted disease casts with full stack exchanges, so the legacy attack
path survives for rev ≤6 games and all 33 ledgers replay clean. The
client offers the plain Cast button for it in rev-7 games.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-28 12:35:35 -04:00
Eric WagonerandClaude Fable 5 ca9b09c029 Powerstone blesses a number already played this turn
On the table the stone and the number land together; digitally the
display can come a beat late and the +1 silently vanished. Displaying
POWERSTONE now grants the stride retroactively when a number was
already played for movement this turn, with a chronicle line so the
gain is visible. Pure widening — no rev needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-28 10:22:59 -04:00
Eric WagonerandClaude Fable 5 ca0a4ef2e3 Forced drops bank honestly; half-dead sockets confess
DROP OBJECT's forced drop hardcoded onHomeOf null and skipped the
victory check — a treasure shaken loose over a home square neither
reported the banking nor triggered scoring or the captured-treasures
clause until something else prodded the engine. It now lands like any
drop: homeOwnerAt reported, checkVictory immediate.

And the stuck-game mystery from 38YP: a deploy restart left the
browser holding a half-dead socket that never fired onclose, so the
existing reconnect loop and masthead banner never engaged and a live
game wore a frozen face. The client now pings every 15s and force-
closes a socket silent for 45s, which wakes the reconnect machinery;
the server answers pong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 23:45:26 -04:00
Eric WagonerandClaude Fable 5 5eb5312637 The attached number rides every cast path too
A number attached to SHIFT WAND silently stayed in hand: the two-stage
caster paths (shift-wand, teleport-opponent, mental-force) went through
withMods, which carried the modifier cards but not the separately-held
attached number. withMods now folds the number in wherever the call
site did not already carry it, so what the hand shows attached is what
the command carries — all of it, on every path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 23:22:56 -04:00
Eric WagonerandClaude Fable 5 5f1eda2731 Boobytrap decoys die their own deaths
Stepping on a face-down token flips it: a blank leaves the board the
moment it is discovered, instead of all four indicators lingering
until the real one detonates. The trap itself still waits, the caster
still strolls over everything unharmed, and the chronicle narrates
the flip so the table learns which squares stopped being scary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 22:52:09 -04:00
Eric WagonerandClaude Fable 5 e388691858 The tear waits for counteractions (rules rev 6)
STRENGTH's card says the grab "would be an attack," but the engine
resolved it instantly while punches opened counteraction windows — the
defender never got their say. At rev 6 the tear rides the stack like
any physical attack: the clutch roll waits out the counters, and only
an unstopped grab still in arm's reach of a still-laden defender gets
its wrestle. A TELEPORT escape now genuinely saves the chest (FULL
SHIELD does not — the card stops spell attacks only, and the grab is
physical). The automatons counter tears at their carried gold by not
being there when the hand closes. Narrowing, so rev-gated: games
pinned at rev 5 keep their instant tears and replay unchanged — all 29
ledgers verify.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 22:40:12 -04:00
Eric WagonerandClaude Fable 5 825a73b154 The cockpit's spells fire: fx timers survive the state message
Two lightning blasts at an adjacent wizard drew nothing in first
person. The fx pipeline staged the bolt correctly — then died unfired:
the server sends `events` and `state` back to back, the state message
re-derives `me`, and the fx effect tracked it, so Svelte ran the
cleanup — which cleared every pending timer and stripped in-flight
sprites — before the bolt's zero-delay start could fire. Every live
cast effect in the pane died this way since the pane was born.

The effect now tracks only the batch; everything else reads under
untrack, and timers are cleared only when the component goes down.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 19:15:55 -04:00
Eric WagonerandClaude Fable 5 773f0a7d21 Visionstone shots draw their ray and mark the pierced wall
A stone-bearing automaton fired Blind through a warp mouth and one
wall, and the table saw nothing but the attack modal — the sight trace
only knew plain and bent-corner lines, so a legal shot read as a cheat.
When the attacker displays VISIONSTONE and no plain line exists,
stackSightTrace now finds the single edge whose removal opens the line
(warp legs included), draws the ray through it, and the keymap pulses
the dissolved wall so the answer to "how can he even see me?" is on
the board. Verified against the live LZ5R blind moment: warp mouths
at (0,2)W and (4,7)E, pierced wall V:3,8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 18:52:46 -04:00
Eric WagonerandClaude Fable 5 358633022c Attachments ride every targeted cast, not just player targets
The engine learned to bend neutral spells, but the client only sent
aroundCornerInstanceId (and amplify/add/extend) on player-target casts
— applyMods was never called on the cell, edge, creature, two-cell,
trap, or teleport-opponent paths, so a SAFE aimed around the corner
went out bare and the engine rightly refused it. All targeted cast
dispatches now pass through withMods, so what the hand shows attached
is what the command carries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 17:41:52 -04:00
Eric WagonerandClaude Fable 5 3bf3c33fdc Around the Corner bends every spell, hairpins included
Two holes, found when a SAFE cast at a treasure 'around the 180 degree
corner' refused with no line of sight (room LZ5R). First: the bend was
honored only in the attack path — every neutral resolve (creations,
summons, utilities, edge targets) checked straight sight and ignored
the attached card, though the card says 'any line of sight spell'.
castSight/castSightEdge now thread the bend through all of them, and
the chronicle narrates the neutral bend too.

Second: the bend itself pivoted only at cell centers, so the card's
flagship shot — doubling back up to 180 degrees around a wall's end —
never resolved: both legs graze the corner and die. The pivot may now
sit in the wall's open gap itself (bentSightThroughGap), which is what
rounding a corner physically is. Closed doors still block; two corners
still refuse.

The client lights bent-reachable squares for real now instead of the
old one-step-adjacency guess, creations included. Pure widening, so no
rev bump: every one of these casts was refused before, so no ledger
holds one. All 26 production ledgers replay clean; the LZ5R moment
itself replayed and the exact refused cast now lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 17:34:48 -04:00
Eric WagonerandClaude Fable 5 f53f26ace9 The cockpit's camera ignores your own casts entirely
A cast through the glass is aimed by the eyes that fired it — you
face it by definition — and a keymap cast is a deliberate off-screen
act. Neither moves the camera. Aims and cutaways serve only deeds
done by others.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 16:10:27 -04:00
Eric WagonerandClaude Fable 5 72f5df5bba Your own casts never cut away
Casting a door onto the wall you already faced tripped the blocked-
sight cutaway: the camera leapt to film the wall from another
vantage, reading as a spin that put your back to your own door. In
the live cockpit your own deeds now only ever TURN the head, gently
and never past a quarter-turn — cutaways remain for things done TO
you.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 16:09:57 -04:00
Eric WagonerandClaude Fable 5 e2dfbd14e3 Edge cards light their targets; own casts never wrench the neck
Raising an edge-target card now makes every wall and door face
breathe faint gold in the pane — the cockpit's answer to the board's
edge handles — with hover still brightening the chosen face. And the
director's own-magic aim gains a quarter-turn cap: a deed you aimed
behind you via the keymap stays behind you, while deeds done TO you
may still spin the world.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 16:08:51 -04:00
Eric WagonerandClaude Fable 5 64a6041a28 The first-person pane becomes an invitation, not a default
Through-your-eyes ships opt-in: the preferences toggle turns it on,
and only an explicit yes is remembered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:57:32 -04:00
Eric WagonerandClaude Fable 5 37d3265c67 Rules divide the checkbox stanza only
Between checkboxed rows and nowhere else — no frame above or below
the stanza, none around the control rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:52:58 -04:00
Eric WagonerandClaude Fable 5 95bfecb206 The preferences read as a ruled ledger
Rows rule apart with the games ledger's own brown line, fill the
panel's width so the checkbox column aligns, and control rows
right-justify their controls opposite their labels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:51:36 -04:00
Eric WagonerandClaude Fable 5 054b06c631 Preferences wear the table's ink; hand-drawn becomes alternate
The native checkboxes clashed with the parchment: they are now
stamp-bordered boxes checked solid with a parchment tick, and their
labels rag left with the box instead of centering. The token-art
toggle's second option reads "alternate".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:46:58 -04:00
Eric WagonerandClaude Fable 5 2206982c05 Forward rides above the glass, back below it
The strides map to the space they move you through: step-forward
centered in the top bezel, step-back centered in the bottom, matching
the hand's instinct from the old edge chevrons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:45:26 -04:00
Eric WagonerandClaude Fable 5 3bd7e3c81d Credibility pass: the duels leave the code, staying in the ledgers
Third pass, scoped from 7a32370. Three blind reviewers (engine, web,
server/tools) each concluded the work is coherent engineering with
seam-level tells; every finding was verified before touching a line.

Session biography left the comments: the bot brain's heuristics no
longer cite the opponent who taught them, the RNRX coma parenthetical
and the thief-chase citation are gone, the seat-wallet comments state
their invariants without the war stories, and process-named test
groups now name the behaviors they pin. The incident record lives
where history belongs — commit messages and the ledgers.

Structural dedup: one VISIONSTONE one-edge-sight loop serves both
LOS paths; one creature-arrival touch handler serves walking and
warp-stepping (error text aligned); one facingWedge helper draws both
keymap ribbons; one spriteVisibleInCol rule serves the draw pass and
the hover test (which also stops re-sorting per pointermove); and
deepestFacing joins the director, replacing four copied scans.

Test hardening exposed real rot the tells were hiding: the tight
CreatureState cast caught two literals with a bogus field masking
three missing ones; the number-hoarding rig had NEVER run (its
column didn't exist on seed 42 — it now carves its own geometry);
the bank-guard rig now drives the whole table to an arrival
assertion; the bent-trace test walls off straight sight so the bend
must answer. Silent `return`-on-rig-failure became loud throws, and
can-never-fail assertions were removed.

Sweep-up: the eyeTurn ghost comment, the stacked leave() doc
comments (leave now delegates to leaveLocal), the dead ternary in
the seat client, the kick handler's name-coercion drift, kick ledger
lines gain timestamps, archiveRoomFile reuses fileFor, the RULES_REV
alias retires in favor of the engine constant, hitTest un-exports,
the NUL-sentinel hover shape becomes an honest "none" variant, and
the steering holds get named constants. The bezel's stride cluster
also centers per the table's note.

288 tests, 24 ledgers verified, all workspaces typecheck.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:43:24 -04:00
Eric WagonerandClaude Fable 5 7bd8eff11c The instruments move into the bezel; the windshield goes bare
Per the table's suggestion: chevrons and trays no longer overlay the
canvas — a frame holds them. The top bezel carries the ride controls,
the sharing-your-square portraits, and the hide button; tall turn
paddles flank the glass; the bottom bezel holds the strides, the
at-your-feet tray, hazard chips, the warp-step, and the key hint.
Every canvas pixel is now permanently hover-and-click target glass,
and the yield-while-targeting hack retires unneeded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:23:14 -04:00
Eric WagonerandClaude Fable 5 0530ecb039 The helm yields the glass while a card is aimed
The drive chevrons are buttons over the canvas edges, and the
back-stride strip owns the bottom band — exactly where an adjacent
square's floor renders. With a card selected, hovering there showed
no target and a click would have STEPPED backward instead of casting
(the tacks report: the pane refused what the map allowed). While a
card is aimed the strips now fade and stop catching the pointer, so
every pixel of the pane hovers and clicks as a target; the arrow keys
still steer throughout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:21:05 -04:00
Eric WagonerandClaude Fable 5 728f9fc28a The feet tray learns hazards and the warp step
Two blind spots close. The active body — wizard or ridden mount —
standing on a dimensional-warp token gets a "step through the warp"
button in the feet tray, dispatching warpStep for the wizard and
creatureWarpStep for the mount (the command the RNRX troll report
earned). And whatever you stand IN shows as a warning chip: tacks,
slime, dust, a pit, the ooze — the near plane's last secrets told.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-27 15:19:22 -04:00