Commit Graph
100 Commits
Author SHA1 Message Date
Eric WagonerandClaude Fable 5.1 3d88f629b7 The face-up hands at game's end keep to their frame and fold away
On a desktop the table is pinned to the window and the board's row may
shrink to nothing; four hands of cards beneath it did exactly that in
room 7S6C, and nothing put them away. The reveal now scrolls inside a
capped frame and its header carries a fold button.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-16 00:36:57 -04:00
Eric WagonerandClaude Fable 5.1 6e49fc9020 Credibility pass: comments say what the code can't, and the seams are gone
Engine: pit-rim exits live in one helper (pitRimExits) shared by the
resolver and the automaton; chargeReach names the six-stride cap; dust
sight is inDust + dustAtEnd; PushPending is exported once; the force-field
counter builds its events in one list; drawUnderSlowDeath says what it
draws under. Test groups are named for the rule they pin, not the
revision that introduced it.

Client: one smoothstep; one edgeScar per battle-scarred edge; one
board-zone rule (which also seats the caption strip on phones); the
compass names live in SIDE_NAMES; net.flash() is the one toast; MediaQuery
replaces two hand-rolled matchMedia listeners; sprites carry their sizes
as plain numbers and their CSS in one rule each; canvas helpers are
formatted like the rest of the tree. Comments that told how a cel or a
fallback used to look now say what it draws. The actions-over notice no
longer blames a pickup when slime ended the turn.

Server and ops: dataRoot() is the one data directory; headlineOf builds
its deeds without a cast; the rollup and skills read the same way the
code behaves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-16 00:21:34 -04:00
Eric WagonerandClaude Fable 5.1 85690f0e23 Rev 21: LIFESAVER saves, and FORCE FIELD stands for the turn
An audit of every card in the deck, prompted by the Alter Ego, found
two more whose cast worked but whose promise was never kept.
LIFESAVER — "immune to the effects of losing both of your treasures"
— was cast and remembered and never asked where a wizard is
eliminated for exactly that; twenty-three recorded casts never
mattered. FORCE FIELD stopped the spell and vanished, though the card
keeps it standing until the opponent's turn ends, barring them from
entering its caster's square or casting on or past them. Both are
honored from rev 21, the field on every side where the card says one.
Older games replay as they were played. Tests pin both readings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 23:57:38 -04:00
Eric WagonerandClaude Fable 5.1 10d5c5815d The Alter Ego casts from its own square; the first-stride cue leaves the maze
Randy raised his double, selected it, and could cast nothing through
it: the card's whole point — "it may use any of the spells in your
hand, and you need not be in its L.O.S." — had never been wired. A
cast may now name the double, and the double's square becomes the
spell's origin: sight, reach, the launch point the reel films, and
the direction a knockback or rout pushes. The hand, the attack, and
every answer to it stay the caster's. Spells that move the caster's
own body — a goat's charge, a swap — do not travel through it. On the
board, the double's sight lights the eligible squares; selecting the
double and then a card aims from where it stands. A test hides the
caster and fires through the double.

The first-stride caption sat on the maze and hid the walls beside the
wizard; it now stands above the board with a color chip for "you."

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 23:38:58 -04:00
Eric WagonerandClaude Fable 5.1 5f58a0955a A notice taller than the screen scrolls inside its frame
Preferences on a phone ran off the bottom with no way to reach the
rest. Every notice now caps at the window's height and scrolls within.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 23:29:18 -04:00
Eric WagonerandClaude Fable 5.1 ff35e8ef8e The table fits the window: the hand stays in reach, the maze takes the height
On a desktop the game grid is now exactly the window's height. The
board and the paper rail share the top row, the rail scrolling in
place, and the hand with its End turn sit pinned beneath — nothing a
first turn needs is ever below the fold. The board's window fills
whatever height the row allows instead of a fixed subtraction. A
wide, short window — a laptop in landscape — stands the hand beside
the board on its own until the player chooses a side, since a tall
maze under a bottom hand left a postage stamp. A short window shrinks
the bottom hand's cards a little. The eyes toggle floats over the
board's corner so it costs no height, and the masthead's "invite
friends" hides on phones where it wrapped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 23:19:59 -04:00
Eric WagonerandClaude Fable 5.1 4cc0423846 The first minute, for a stranger: what the game is, what to do, and how to join
The landing says the pitch before it asks for a name — steal treasure,
blast through walls, outwit your friends — and states the goal. The
coach says "select a card, then choose its target," which is what the
cards actually want. The turn banner counts treasures home beside the
moves left. The council speaks once when its three voices agree. A
friend arriving on an invite link meets "Join Eric's game," a name
field, and a seat, with the gallery an afterthought; the masthead
says "invite friends" beside the room code. The first-person view has
a toggle beside the board instead of a checkbox in preferences. How
to play opens with winning. And a shared moment's card is titled by
the turn's story — the treasure carried home, the blow that landed,
the spell cast — instead of whose turn it was.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 23:03:17 -04:00
Eric WagonerandClaude Fable 5.1 a071494f4a Reduced motion is honored everywhere the table moves
One shared motion policy: the OS's reduce-motion setting, read live
through Svelte's own media query, folds into the flourishes switch.
Under it the board plays no flourishes, the reel skips its die
interlude and slate wipes, bodies jump between squares instead of
gliding, the canvas holds its ambient shimmers still, and the camera
cuts wherever the director would have panned — in the cockpit as in
the reel, so the eye still lands on what the director chose to watch.
Timers and animation frames are cleared when the policy flips or the
pane closes. A first turn and two reels stepped clean under the
setting with no errors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 22:42:08 -04:00
Eric WagonerandClaude Fable 5.1 65e88df523 The step light is for a first game's first turn, then by choice
It lit every turn of the first game. Now a browser's first game lights
its first turn on its own, and the preference is an explicit switch
that keeps it on every turn for anyone who wants it. The old
preference key retires with it, so nobody is left lit by accident.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 22:21:22 -04:00
Eric WagonerandClaude Fable 5.1 53b6b01582 The workshops wear the masthead, with the door marked
The token, flourish, and eyes workshops opened as bare rooms with no
way back. The masthead now heads each, wordmark linking home, with
the table, the clips, and the other workshops a tap away.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 13:44:12 -04:00
Eric WagonerandClaude Fable 5.1 34b621b5cb The board's furniture and the reel's fists, painted as cels
The last of the inked look. Walls carry stone joints and highlights,
doors their grain, hinges, and a brass lock plate whose keyhole turns
to a cross when jammed; battle scars fracture and branch with the
damage; illusion walls drift as tapered wisps with breathing glints;
the wall of fire burns in flat three-tone flames on a stepped flicker;
warp mouths and destinations are painted curls. Through a wizard's
eyes the punch is a three-tone hand with an ink contour and a
hand-cut POW, the same on every platform where the emoji glyph was
not, and the incoming fist no longer reverses late in its travel. The
inked stand-ins for missing conjuration files match. Goldens
re-blessed: every board frame, and the web's third ring.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-15 12:42:02 -04:00
Eric WagonerandClaude Fable 5.1 8dfd0e0c29 The art refresh: textures, tokens, conjurations, and cel flourishes
Eric repainted the maze. The first-person materials are painterly
sandstone, flagstone, and iron; the hand-drawn token set is redrawn
with matching chests and hats; the standing conjurations — hedges,
jello, dust, safe, warp light — and the effect billboards are redone
in the same hand; and the board's flourishes trade their filters and
gradients for flat cel silhouettes cut from five shared shapes, which
read better against the counters and cost a phone nothing. Thirty
scene goldens re-blessed against the new walls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-14 20:28:52 -04:00
Eric WagonerandClaude Fable 5.1 3fc9df85ac The chronicle marks each line with its wizard's color; the turn's owner stands out
Two of Randy's flourishes. Every chronicle line now carries a small
square in the color of the wizard acting — whoever cast, struck,
walked, or countered, else the turn's owner — so one player's doings
can be picked out of the scroll at a glance. And the life-points row
of the wizard whose turn it is wears an accent bar and a tint, since
bold alone was lost in the handwriting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-14 19:23:00 -04:00
Eric WagonerandClaude Fable 5.1 19aec73b30 Screenplay: Dust and Light
The eleventh scene, written for the new conjurations. Wanderer fills
the corridor between them with a dust cloud — no sight, no spells —
so Rival lays a dimensional warp with one mouth under his own feet
and the other at her back, steps through the light, and lands on her
square fists first. She bolts into her own cloud, and the die picks
where she goes: into a wall. Three beats through the eyes — the cloud
billowing, the arrival, the blind stagger with the far pillar of light
at the corridor's end — and the board at the arrival.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-12 14:57:11 -05:00
Eric WagonerandClaude Fable 5.1 09fcaaf4e3 The house rulings say how a loaded slime goes off
One spell per wizard who pushes in, oldest first — the card says only
that each goes off once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 16:05:03 -05:00
Eric WagonerandClaude Fable 5.1 44c7ff8379 A slime shows how many spells wait in it, and a peek names them
Every cast into the gel was seen at the table, so the count is public:
a badge on the slime, and the peek lists each spell with its caster.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 16:00:32 -05:00
Eric WagonerandClaude Fable 5.1 16fac5e6ca A test walks a wizard into a slime with a spell waiting in it
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 15:55:00 -05:00
Eric WagonerandClaude Fable 5.1 c09bcc481d An attack tapped onto a slime is cast into it
The engine has trapped spells in slime since the card arrived; the
board never offered the tap. LIGHTNING BLAST on a slime square now
lodges there for the next wizard in, as the card says.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 15:51:07 -05:00
Eric WagonerandClaude Fable 5.1 5c956c24bb Sentry ignores the in-app browser's own scripts
Facebook's Android browser injects a performance logger under iabjs://
and its bridge dies on unload; the error wore our name (WIZWAR-6).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 15:31:33 -05:00
Eric WagonerandClaude Fable 5.1 3f71b04c3b Rev 20: a wave names its victims before it pushes (LK97)
Kestrel stood against a wall Mad Hywel turned to water, was washed one
square back into stone, and was crushed twice. The sweep walked the
way it pushed: having shoved him onto the next square, its next probe
found him there and shoved him again with the force left. From rev 20
a sweep names every victim along its line first, then pushes each
once; older games replay the double shove. A test washes a wizard one
square into a wall under both readings.

With it, a client fix: a tap on a bare square while holding a card no
longer walks there. Kestrel tapped a slime with LIGHTNING BLAST in hand
and strode into it. The tap now says the card is aimed at a wizard or
creature and leaves the feet alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 14:38:48 -05:00
Eric WagonerandClaude Fable 5.1 e5d82a94af A first game's first turn: the step light, the goal, and a held opening
Three strangers this week pressed play, looked at the board for a
minute, and closed it. What they saw: a small maze under a roll-off
card, a column of prose, seven strange cards, and nothing that said
where they were or what to tap. Now, on a browser's first game, the
squares a stride can reach glow and the rest of the maze falls into
shadow, with one line hung over the wizard for the first stride. The
turn banner says what winning is for the first two rounds. And when
the clockwork rolls first, its opening turn waits behind the roll-off
card instead of playing out unwatched, then arrives a beat at a time.
The step light switches off once that first game is won, and lives in
preferences for anyone who wants it back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-07 12:50:28 -05:00
Eric WagonerandClaude Fable 5.1 96c0c18d97 Referrers count only on pages we served
A scanner forging google.com on /.env showed as forty-eight Google
readers before breakfast.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-06 23:30:14 -04:00
Eric WagonerandClaude Fable 5.1 7af9de4a9a The rollup's check-in tells Sentry its schedule
A bare ping to a slug Sentry has never been told about is accepted
and dropped, so the rollup monitor never existed and a missed night
would have alarmed nobody. The check-in now carries the monitor's
shape and upserts it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-06 11:33:11 -04:00
Eric WagonerandClaude Fable 5.1 2bc5fba8d2 The rollup's path mix counts people only
Crawlers and the uptime monitor hit the front page hundreds of times a
day and were tallied beside the visitors who read it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-06 09:24:55 -04:00
Eric WagonerandClaude Fable 5.1 f859d90af8 Rev 19: a dust cloud blinds whoever stands in it (CPU7)
The automaton stood in Kestrel's DUST CLOUD and filled a square with
stone three rows away. The cloud blocked only sight lines passing
through it; the square a line began or ended in was never asked. The
FAQ is plain: no LOS spell may be cast by a wizard in a cloud, nor at
one, though a spell upon oneself still works, and VISIONSTONE does
not see through dust. From rev 19 the engine's sight and the view's —
the targeting aid the client and the automaton both read — refuse a
line with dust at either end. Older games keep their sight.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 23:30:17 -04:00
Eric WagonerandClaude Fable 5.1 3d4bf1fa6d A sprung Ward says its bite is coming, not that it missed
The bite rides a counteraction stack, so the batch that springs the
Ward carries no damage; the notice read that as a bite with no
purchase and the three points landed later unannounced. It now says
the bite waits on the taker's answer, and the counteractions follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 23:25:05 -04:00
Eric WagonerandClaude Fable 5.1 34d76d9feb Rev 18: REFLECTION's returning half is the caster's to counteract (96WE)
Kestrel's blade came back halved off the automaton's REFLECTION and
landed before he could raise the ABSORB in his hand. FULL REFLECTION's
return was already a fresh attack with the caster's own window; the
half now goes the same way — its damage and any split duration ride
a returned stack the caster may meet with an ABSORB, a BLUNT, or
another REFLECTION. Trapped spells and creatures' touches keep the
instant half. Older games replay as they landed. A test soaks the
half and takes it, under rev 18 and rev 17.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:37:19 -04:00
Eric WagonerandClaude Fable 5.1 2fd324a475 POWER ATTACK burns any number of life points short of the last
The card sets no ceiling and the engine never had one; the picker
offered five. It now offers every point but the one that keeps you
alive.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:25:43 -04:00
Eric WagonerandClaude Fable 5.1 2812b85016 The mouth on a pit's rim may be named in any game
Only a bare step is a matter of revision: from rev 17 a square beside a
mouth is a fork to name; before it the step lands on the square, as the
recorded games did. Naming the mouth was never recorded, so it opens
for every game.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:24:18 -04:00
Eric WagonerandClaude Fable 5.1 461c1a5c83 Rev 17: a warp mouth on a pit's rim is a way off it (M4Q7)
Kestrel stood beside a pit dug against the board's west edge, walls
north and south of it, and was told there was nothing to land on — the
rim check counted only neighbouring squares, and the warp mouth beyond
the pit is not one. From rev 17 a mouth on the rim is an exit like any
square, named at a fork like any other; every older game takes the
mouth when no square offers, so the refused crossing goes through as
it stands. The jump through a mouth carries `via: "warp"`: the board
shimmers both mouths instead of streaking across the maze, and the
reel cuts rather than gliding. The automaton leaves a pit only by its
exits. Three tests walk the rim under both readings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:20:18 -04:00
Eric WagonerandClaude Fable 5.1 dc4d2a1221 The stub path checks the room id, as the ledger path does
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:10:03 -04:00
Eric WagonerandClaude Fable 5.1 6293c36add Sleeping rooms stay asleep across a restart
Idle rooms already went back to their ledgers, but a boot replayed
every ledger before the first sweep could put them to bed again — most
of a restart's fifteen seconds spent rebuilding games nobody was at.
A room put to sleep now leaves a stub beside the ledgers, with the
ledger's size at that moment; at boot a stub whose size still matches
answers for the room and the ledger stays unreplayed. Replayed rooms
take their eviction clock from the ledger's last line, so the first
sweep after a boot writes the stubs for everything long idle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 22:05:43 -04:00
Eric WagonerandClaude Fable 5.1 c7edfa74ad The state is copied by hand, and the proxy waits out a restart
Two-thirds of a ledger replay was structuredClone; a recursive copy of
plain data does the same work in a fraction of the time, so a server
boot — every ledger replayed — is half as long, and the automaton's
lookahead is quicker with it. Caddy now holds a request for up to
thirty seconds while the game restarts, dialing every quarter second,
so a visitor who lands during a deploy waits instead of meeting a 502.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 20:41:02 -04:00
Eric WagonerandClaude Fable 5.1 0821b8f7c7 The house rulings name how a goat's ram is measured
The shortest walk on this turn's legs, no ceiling, MAD DASH doubling
the lot — the reading the engine has held since rev 10 and the
combo it only just began to allow at full length.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 19:49:04 -04:00
Eric WagonerandClaude Fable 5.1 3b52a92cdc Rev 16: BUTT-HEAD's charge is measured as far as its legs reach (K4T3)
Reuben, a goat with a 5 played and eight legs, aimed at a wizard eight
corridor steps away and was told he had no wings. The charge measures
the walk through real corridors, as rev 10 made it, but the walk was
searched only six steps deep and anything past that came back as
unreachable. From rev 16 the search runs as far as the legs do; older
games keep the six-step reading. A test charges seven or eight steps
under both revisions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-05 18:29:05 -04:00
Eric WagonerandClaude Fable 5.1 0911312f98 A page from before a deploy can still find its chunks, or reload once
Sentry WIZWAR-5: a table open through a deploy asked for the replay
module by its old hashed name, the deploy had deleted it, and the
server answered with the app shell as text/html — "not a valid
JavaScript MIME type". Three guards: deploys keep old chunks for a
week instead of deleting them; a missing asset is a plain 404, never
the shell; and the client reloads once when Vite reports a failed
import, so the live bundle takes over.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 20:30:39 -04:00
Eric WagonerandClaude Fable 5.1 fa5a5a07a7 MEGA-MONSTER asks which number to double
The two choices sat in the hint strip with life-points pre-selected,
so a tap on the monster cast before the choice was seen. Tapping the
monster now asks — life-points or movement, with the monster's
current numbers shown — and the cast waits on the answer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 17:25:34 -04:00
Eric WagonerandClaude Fable 5.1 9f98a4fc59 The unasked council's clearing effect watches the turn
It checked its own plain flag first and returned, so the turn was
never read and never tracked; the effect ran once and slept. The turn
is read first now, and the flag is reactive.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 17:22:39 -04:00
Eric WagonerandClaude Fable 5.1 e724267b0a BIG MAN pushes around corners; at a fork the pushed wizard chooses
Eric, a giant on his first turn, could only shove a wizard in a
straight line: the engine shoved in the giant's own direction or
refused. The FAQ says otherwise — "If Big Man pushes someone to an
intersection, then the other player gets to decide which direction to
go." Now a pushed wizard leaves by any open side but the way the giant
came. One open side is taken. Several are theirs to choose: the stride
hangs in a pushPending window like the Ward's, the pushed wizard
answers with pushChoice (a dialog offers the ways; an automaton steps
out of the giant's line), and the stride resumes with the answer — a
blinded giant's roll is not rolled twice.

Rounding a corner was a refusal before, so every game may do it now.
Only the case that used to resolve — straight ahead open — keeps its
old reading in games dealt before rev 15; from rev 15 the pushed
wizard chooses there too. Tests pin the corner, the fork's window and
its refusals, and the older reading; 84 ledgers replay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 17:19:34 -04:00
Eric WagonerandClaude Fable 5.1 c6913ea1bc Rev 14: a pit's rim is a ledge, and the walker chooses the way off (GDCN)
The table's reading of CREATE PIT: a thin ledge runs around the rim,
and "jumping over" is edging along it, trying not to fall. So on a 2,
3, or 4 the walker lands on an open square beside the pit — the only
one if there is one, otherwise the one they name (a click on that
square sends `exit` with the move) — and a pit with no way off cannot
be entered from there at all, refused before any die is rolled. The
FAQ's diagonal crossing at an intersection is this rule.

Older games bounced the walker back and charged the stride; they keep
that under their frozen revision, so GDCN's seven bounces replay. The
clockwork's path search treats a pit as a road when any side of its
rim is open and names the exit its path leaves by. Tests pin the fork,
the single way off, the closed pit under both revisions, and the
brain's route.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 16:18:42 -04:00
Eric WagonerandClaude Fable 5.1 0750fe3f21 The clockwork minds the pit, the curse, and the shadow (GDCN)
Kestrel's archmage berserker dug a pit at the far mouth of a warp,
then tried to walk through that mouth seven times: each stride landed
on its own pit with a thornbush beyond, the maze bounced it back and
charged the stride, and on the seventh it rolled the 1 and fell in.
Cursed with SLOW DEATH at seven life, it kept drawing two cards a
turn at a point apiece, raised a SHADOW that drinks a point a turn,
and bled to death in three turns.

The path search now refuses a pit it cannot leap beyond — the same
test the maze applies — whether the pit is a waypoint or the goal.
Under SLOW DEATH the end-of-turn draw keeps four life in hand and
draws nothing when it cannot; while bleeding, or at six life or less,
no SHADOW is raised. Three tests pin them.

The chronicle tells the bounce as what it was: "leaps the pit —
nothing to land on beyond it — and teeters back", not a blind blunder
into a wall; and the shadow's last drink no longer trails a zero-damage
line. Brain and chronicle changes only, ungated; 83 ledgers replay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 16:11:42 -04:00
Eric WagonerandClaude Fable 5.1 b92016db51 The unasked council leaves with the turn
Advice offered on a first turn stayed on the sidebar until dismissed,
as advice a player asked for should. Offered advice now clears when
the turn passes; asking again keeps it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-04 16:00:09 -04:00
Eric WagonerandClaude Fable 5.1 2d98393010 The recorder takes one poster per clip
Once written, the poster's countdown fell back to idle while the same
move still showed, re-armed, and overwrote the poster with the next
move — for two scenes, the change-of-eyes wipe. A done flag holds it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 19:39:13 -04:00
Eric WagonerandClaude Fable 5.1 53fba77a55 Eight quality-of-life turns for strangers, newcomers, and veterans
For visitors: the landing shows a clip card of the maze before asking
for a name; "Play now against a clockwork wizard" is one tap from the
landing to the first turn (the room, the seat, and the flip each
follow as the server confirms the last); pass-the-device and seat
transfer fold behind "More ways to play".

For a first game: automatons default to apprentice until this browser
has seen a game through, then step up to adept unless the tier was
picked by hand; the council speaks once unasked on a first turn; a
finished game offers "play again" (the same bots re-seated in a fresh
room) and "share the tale" (the whole replay as a link).

For players who know the box: a House rulings tab in help — what the
digital table does differently, every rulings revision (now exported
from the engine as RULES_REVISIONS, which the deckRev doc block points
at), and the 76 cards the FAQ has spoken on. And the arrow keys and
WASD walk on the board as they already did in the first-person pane.

The recorder's poster settle lengthens to outlast a change-of-eyes
wipe; two cards had caught the title card instead of the maze.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 19:24:46 -04:00
Eric WagonerandClaude Fable 5.1 2b5c2c4884 The visitors digest: who is here, who came today, how far they got
/usr/local/bin/wizwar-visitors.sh prints one screen — live sockets and
rooms touched in the hour, today's traffic row, the names seated today
with the first-time ones marked, every room opened today with its
state, move counts, span, and last human move, humans' chat verbatim,
and the desk's count — and the wizwar-visitors skill reads it aloud.
It replaces the four hand-written versions of the same report from
announcement day.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 18:58:53 -04:00
Eric WagonerandClaude Fable 5.1 9d2c66f0bd The lobby leads with your wizard, then fills the table
Pick your color first; then one section, "Fill the table", holds the
invite link, the roster, and the computer wizards, with a line saying
friends, automatons, or a mix all count toward the two to six. Abandon
sits below Start on its own line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 18:53:48 -04:00
Eric WagonerandClaude Fable 5.1 c2506dae69 Color, as the rest of the table spells it
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 18:50:18 -04:00
Eric WagonerandClaude Fable 5.1 a7c55819ca The lobby says the plain words beside the flavor
Two evening arrivals created a room and left in the lobby: one never
seated anyone, one seated a bot and never pressed the button. The card
led with "share the link" as if company were required, the row of
temperaments had no word saying they were computer opponents, and the
start button said only "Flip the boards".

Now the card has three headings — Invite friends, Your colour, Or seat
a computer wizard — with one line on what a hunter, berserker, and
worrier do; the start button says Start the game with "flip the
boards · N wizards" beneath; and while it is disabled a line says what
would enable it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 18:48:05 -04:00
Eric WagonerandClaude Fable 5.1 6e1b565228 The first-turn slip promises what the board can do
Arrow keys stride only in the first-person pane; on the board a step
is a tap on the square beside you. The slip says so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 15:11:35 -04:00
Eric WagonerandClaude Fable 5.1 6e3e998307 Three slips for a stranger's first game
Two newcomers on announcement day left within minutes at the same
moment: they picked up a treasure, the turn's actions ended as the
rules say, every walk after that was refused, and the status line's
"turn spent" told them nothing about why or what the treasure was for.
Both had stalled over a minute before their first move as well.

Now: on a first turn, a coaching slip says what a turn is — walk this
many squares, a NUMBER card walks farther, tap a card to cast, End
turn — dismissed once with "got it" and remembered in prefs. After a
pickup, the status line says the pickup ended the actions and the
treasure comes along, and to end the turn and carry it home. While
carrying on later turns, a slip names whose treasure it is, how far
home is, and that two scored wins; your own treasure scores nothing
in your arms.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 14:47:24 -04:00
Eric WagonerandClaude Fable 5.1 cf7920e159 The rollup counts the tags on posted links, and the pulse has a right-now
Reddit and BoardGameGeek send no referrer, so a visitor from either
looks direct. A ?ref=<name> on the posted link says where they came
from; the rollup counts those (one per address per day, Facebook's
fbclid as its own) beside the referrers browsers do send. The pulse
gains a right-now recipe: live sockets, rooms touched in the last
hour, and today's partial row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 12:06:12 -04:00
Eric WagonerandClaude Fable 5.1 ac2c3576a7 The rollup seats the host
A room's host takes the first seat by creating it; only the others
leave join lines. Counting joins alone reported rooms full of nobody.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 12:03:32 -04:00
Eric WagonerandClaude Fable 5.1 e1a740119c Credibility pass: the session seams sanded from the clips-and-camera batch
Two blind reviews of everything since the last pass (afa0e17), every
finding checked against the code, no behavior changed: all thirty
scene goldens match without a re-bless and the engine suite is
untouched.

Reel and renderer: the camera's look-down rule is stated once, beside
LOOK_DOWN, instead of twice in the effect; the empty aim branch that
stood where a cutaway used to be is gone (the guard it implied is now
explicit); the pit events and the punch are handled by their own
types, not through "in" casts; smoothstep is one export used by every
tween instead of eleven inline copies; the two floor rings share one
painter; project() takes a Billboard instead of a third hand-typed
copy of its fields; the strides-left figure and the web rim no longer
shadow the reel's steps and the pane's fx; the die card's verdict is
built from events, not by matching an emoji; the workshop asks for the
hover cue by name instead of passing an empty click handler.

Server and engine: one requestBase() for the origin, one slug pattern
in store.ts gating both the clip page and its files, one 404 for both;
LOOPBACK sits above its only caller; doCounteract names what a counter
is played against once; fearCells sits beside its own docblock rather
than between sightedCellsFor and its.

Deploy: chromiumExe, the private server, ffmpeg, and the reel rewind
live in deploy/lib/harness.mjs, shared by the gate, the recorder, and
the card cutter instead of pasted three times; the recorder drops its
duplicate frame counters and names its poster settle; the card uses the
gallery's exact gold; the one-time Sentry URL bootstrap leaves
deploy.sh; the backup comment states the rule rather than the incident.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 11:55:57 -04:00
Eric WagonerandClaude Fable 5.1 ddd2e347bd The address limits believe the proxy, not the client
X-Forwarded-For's first entry is whatever the client wrote; Caddy
appends the true peer last. The limiter now reads the last entry, and
only when the socket peer is loopback — the proxy — so a request that
somehow reaches the server directly is keyed by its own address.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 11:32:21 -04:00
Eric WagonerandClaude Fable 5.1 ca108f83a6 The nightly rollup keeps what the access log forgets; two doors get a limit
Three sysadmin items ahead of the public announcement's arrivals.

deploy/wizwar-rollup.sh writes one JSON line per UTC day to
/var/lib/wizwar/rollup.jsonl at 00:10: yesterday's traffic from Caddy's
access log (requests, human vs bot addresses as counts only, socket
connects, path mix, external referrers), the table's growth (new
rooms, human seats and names, lifetime game totals, reports and
replies), and the box's vitals (service memory and peak, disk, load,
protocol errors, service starts, ledger size). Re-rolling a day
replaces its line. It checks in to a Sentry cron monitor of its own,
whose URL deploy.sh derives from the backup monitor's on first
install, alongside the cron entry. The pulse gains a Trends section
that reads the last week of it. Caddy keeps 30 log files instead of 5
as the raw backing.

Per-address limits on the two doors anyone may use unseated: 12 new
rooms and 6 reports per address per hour, in a sliding window keyed by
the address Caddy forwards. The per-connection cap stays; it reset on
reconnect, which is what a script would do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 11:30:44 -04:00
Eric WagonerandClaude Fable 5.1 3f909b485b A sprung Ward is named as one, not as a punch
Eric grabbed a warded treasure and took the Ward's three points — but
the attack modal showed a bare-knuckled punch, the hint line said the
owner "attacks with a punch", and the cast panel called it a physical
blow with no card behind it. The engine knew better: the stack has
carried sourceName ("warded treasure") since rev 9, and the damage
line used it. The client never read it.

Now every cardless stack with a name shows that name — and, for the
Ward, the Ward card itself — in the modal, the hint line, and the
cast panel, with the damage inbound. In the chronicle, a counteraction
played against it records "warded treasure" rather than "punch", and
an EMPATHY echo of it names the same. Chronicle-only, ungated; all 58
production ledgers replay clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-03 09:41:12 -04:00
Eric WagonerandClaude Fable 5.1 3723b8f211 The clip gallery joins the site
The box lid links to the clips beside the about line, and the gallery
pages wear the site's own dress — its masthead with a way back to the
table, Oswald headings, Archivo Narrow body, the parchment-and-gold
buttons — so a shared clip and the game it came from read as one
place. The clip page's subtitle is the blurb alone, the title being
the heading.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 21:39:53 -04:00
Eric WagonerandClaude Fable 5.1 86070b42a3 The eyes turn to meet magic thrown at them, even after a stride
Springing the Trap never showed its fireball: Rival stepped into the
watched corridor facing the way he walked, and the ambush's fireball
flew in from the mouth behind him while only the caption told of it.
The director now aims at incoming magic and punches — the caster's or
attacker's square, straight or through a mouth — and a stride that
ends with something to watch turns once more after the walk, the
slate holding the outcome until the eyes face it. The fireball comes
through the wormhole at the camera, then the blow lands.

The trap's first beat re-blessed for it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 21:28:52 -04:00
Eric WagonerandClaude Fable 5.1 86856781df The eyes watch through a warp mouth; a cutaway is for what no sight reaches
The Warp Sniper cut to another angle the moment the fireball landed,
though the caster could see the whole thing: the director counted a
warp mouth before the target as blocked sight. Sight threads warps as
the rules say, and now the camera does too. director.sightline finds
the heading that actually sees a point — straight, or through a mouth
by aiming at the target's position in the far room and asking whether
the bent ray reaches it — and both the aim and the turn-toward-the-
actor rules use it. Only a point no ray reaches earns the cutaway.

Warp Sniper's second beat re-blessed: Wanderer's eyes now find Rival
through the mouth as the turn passes to him.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 20:16:47 -04:00
Eric WagonerandClaude Fable 5.1 7c0a761c59 The eyes look down, the die stops the reel, and a punch is a fist
A day of Eric watching the clips as a newcomer would.

The camera can pitch: FirstPerson takes a `pitch` that shears the
horizon up the pane (walls center on it, the eye stays half a wall
high), so the ground at the pane's foot comes within half a cell. The
director uses it for deeds at your own feet — a conjuration on your
square, a treasure taken up, the pit you fell down — instead of the
cutaway that stepped outside your body.

Everything shows through a warp. The floor pass now runs between the
cast and the wall draw, so a column that bent through a mouth maps its
ground through the warp's rigid motion and wears the far room's decals
— the pit past the wormhole included — under the same violet haze as
its walls.

A wizard down a pit is public (inPit on the player view), drawn sunk to
the floor line in first person and dimmed on the board. A body sharing
your square, which had no depth to draw at, stands right in front of
you. A pit leap glides like a shove instead of cutting. And the
director's blocked-sight test no longer counts a warp BEYOND the
target as a block, nor stands a cutaway camera inside another wizard's
square — the pair that put Wanderer's face across the whole pane.

The reel stops for a die: a card over the held world names who rolls
and for what, tumbles the faces on the reel's own clock, lands the
roll, and only then plays the outcome; the beat, the fx, the glides
and the camera all wait it out. A punch by you drives a fist into the
scene; one at you comes at the camera; both land with a comic POW.

film-clips rolls until the reel's last move rather than a fixed count,
and the gate settles longer on a step whose caption rolls a die. All
thirty goldens re-blessed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 19:20:03 -04:00
Eric WagonerandClaude Fable 5.1 7223f8eb0d The reel slates a change of eyes and captions the wizard's state
Two things for a viewer who has never seen the game. When the reel's
pov passes to another wizard, the cut used to be instant — one wizard
apparently teleporting. Now a slate wipes across the pane: a dark band
sweeps in over the old sight, holds with the new wizard's standee and
THROUGH THE EYES OF <name>, and sweeps off to reveal theirs. It is an
FpFx ("slate") drawn by the renderer on the reel's own clock, so the
harness and the film see the same frames.

The caption grows a right-hand column: strides left to spend this turn
as a countdown, ♥ life, and chips for what the wizard carries,
displays, or suffers — a treasure, displayed cards, spells on them
with turns remaining, lost turns, banked Pass Wall, attack spent. The
save-video composite carries the same column.

All thirty scene goldens re-blessed: the caption is inside every
frame. The 3D panes are pixel-identical to the previous goldens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 13:21:04 -04:00
Eric WagonerandClaude Fable 5.1 6a933f030c A warp crossing is just a step; only a wormhole opening surges
Eric's direction: passing through an ordinary warp mouth is a normal
stride. The veil already marks the doorway, and the wizard walks through
it as through any door — no shimmer, no surge, and the eyes carry on
the way they were going instead of turning back to show the mouth. The
surge stays for the moment a wormhole is cast, staged straight from the
warpOpened event through both its new mouths.

The heist's pinned frames re-blessed for it: at the crossing the reel
now looks down the corridor at the enemy's home rather than back
through the mouth.

deploy/film-clips.mjs films the catalog for the gallery — every scene
in both takes, the reel playing under the paused clock at one frame per
40ms, muxed to 25fps mp4s with the first pinned beat as the poster. The
recorder used for the first clip set lived in a session scratchpad and
was lost; this one lives here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 12:56:53 -04:00
Eric WagonerandClaude Fable 5.1 a7f77367a6 A warp crossing surges the veil itself, not a billboard over the wizard
Every board effect the cockpit had no art for landed as the same purple
lightning-ring sprite, so a body stepping through a warp mouth wore the
ring over its own portrait. Eric wanted the purple wall to do the
animating. The portal effect now becomes a "surge" keyed by the mouth's
index in board.warps: the veil hung in that opening floods with its
texture, the swirl quickens and pales, and a ripple runs out to the
jambs before it settles back — nothing drawn over whoever stands in it.

The director used to steer the eyes toward that ring's cell on the pov
wizard's own crossing; it now aims at a surging mouth the same way, so
the heist's pinned frames render as before. Illusion and Pass Wall
crossings keep the shimmer sprite — stone has no veil to surge.

Scene gate: 29 of 30 frames unchanged. stone-gaze-s10-board drifts
0.21% with or without this change (the dashed sight line's phase), a
pre-existing flake left unblessed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 12:43:28 -04:00
Eric WagonerandClaude Fable 5.1 8488e9a8d1 Clips unfurl: share cards, faststart video, a share button
Each clip page already carried its own title, blurb, poster and
og:video — but the assets behind the tags let the unfurl down. The mp4s
were tail-indexed, so previewers in iMessage, Discord, Slack and
Facebook gave up before finding a player; the tags carried no video
dimensions, without which those same sites refuse to embed one; and the
poster was a 736x578 screenshot of the workshop chrome, cropped to
nothing by any share sheet.

deploy/clips-prep.mjs fixes the set before clips-publish.sh ships it:
a lossless faststart remux of every take, a 1200x630 card per clip cut
from the poster's first-person viewport with the title set over it (an
HTML card screenshot through the harness's chromium — ffmpeg here has
no drawtext), and the fpv frame size stamped into clips.json. The page
emits the card with its dimensions, og:video:width/height, and a Share
button that opens the phone's share sheet or copies the link.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-02 11:26:27 -04:00
Eric WagonerandClaude Fable 5 bea8a2e1e1 deploy.sh installs the backup script it ships
The droplet's cron ran /usr/local/bin/wizwar-backup.sh while the repo
improved deploy/wizwar-backup.sh — two copies, no bridge, and the
Sentry check-ins never reached the box, so the cron monitor starved and
alarmed on 2026-09-02. Every deploy now installs the repo copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-02 09:13:01 -04:00
Eric WagonerandClaude Fable 5 58d1d34cb0 The render harness: every screenplay's beats pinned as golden frames
deploy/verify-scenes.sh replays each screenplay to its authored beats
under a paused Playwright clock — pulses, tweens, and fx advance only
on the harness's schedule, so the same beat renders the same bytes
every run — screenshots the reel, and diffs pixels against the goldens
in deploy/scene-goldens (30 frames: warp transits, impacts, curses
landing, in both views). A channel may drift 6 before a pixel counts,
0.2% of pixels before a beat fails; --update blesses intended changes.

Proven both ways before landing: a clean double-run reproduces all 30
goldens exactly, and resurrecting the warp-smear cutaway bug fails the
warp-transit beat at 61% pixel drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 16:58:43 -04:00
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 d37bc62c28 Test screenshots do not live in the repo root
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 10:43:04 -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 68f171215d The reports digest speaks in the players' own words, untruncated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:46:52 -04:00
Eric WagonerandClaude Fable 5 bcd610aecb The reports desk becomes a skill: fetch, digest, process, reply
/wizwar-reports encodes the whole feedback loop — the JSONL shapes and
legacy-id rule, replay-to-the-pin forensics (roster kicks, refused
commands leaving no trace), cards.json before code, the verdict
vocabulary, rev-gating discipline, and the reply voice — so working
the desk is one invocation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-09-01 09:43:02 -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 e8a7fa1b73 The games ledger peeks without waking a single room
The 45-second myGames poll called getRoom per held seat — every open
browser kept its whole game history warm and dragged sleeping rooms
back out of their ledgers, hollowing the eviction it ran beside.
peekSummary answers live rooms without touching their clocks and
sleeping rooms from a stub snapped at eviction (a sleeping room cannot
change, so the stub stays true until a real join or watch wakes it).
Bad tokens void as before; tokenMatches takes any token map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 15:03:28 -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 e83d70116f Room ids pass a strict alphabet before touching the filesystem
The lazy room wake handed caller-supplied ids to fileFor; an 8-char
traversal like '../../aa' fit through the length trim. Only [A-Z0-9]
codes reach disk now — reads refuse quietly, server-minted writes
fail loud.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 13:48:34 -04:00
Eric WagonerandClaude Fable 5 002698085f Ready for a crowd: idle rooms sleep, share cards render once
Memory now carries only live tables — a sweep puts finished rooms to
bed after 30 minutes and anything untouched after a day, and getRoom
wakes a sleeping room from its ledger the moment anyone asks (the
seed plus the log IS the game). The room cap counts ledgers on disk,
not just rooms awake. Share-card PNGs render once per share id and
cache immutable. Rate-limit kicks use terminate() so an abuser cannot
hold the socket open by ignoring the closing handshake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-30 13:43:23 -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 7bcad12449 The ward's bite gets its name, and shrink slips past the giant
Two chronicle truths from tonight's table: the cardless ward stack fell
through to 'punch from <owner>' — stacks now carry a sourceName, and the
bite reads 'warded treasure' as it always did pre-rev-9. And a SHRUNK
wizard walks straight through BIG MAN's square (table ruling: small
enough to slip between the giant's boots) — a widening, shipped ungated.

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 b538a0a2ae Reversed, Walking Dead heals half a point per step (rules rev 11)
The same FAQ turn as Slow Death's: a REVERSE riding the cast attaches
the curse backward, and every second space walked gains the point the
bite would have taken. Older ledgers attached it biting and replay so.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 23:01:01 -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