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
This commit is contained in:
Eric Wagoner
2026-08-29 19:14:03 -04:00
co-authored by Claude Fable 5
parent f2af1e26f8
commit e93bcc15a2
11 changed files with 107 additions and 139 deletions
+6 -9
View File
@@ -14,7 +14,7 @@
import { castRay, SIDE_ANGLE, OPPOSITE } from "./fpv/raycast";
import { deepestFacing } from "./fpv/director";
import { aimOfEvents, gatherGlides, hurledIn, shortestArc } from "./fpv/director";
import { untrack } from "svelte";
import { onDestroy, untrack } from "svelte";
import type { GameEvent, GameView, Side } from "@wizwar/engine";
let { view, batch, onhide, onstride = null, canStride = false, ontarget = null, onfacing = null, litCells = null, edgeSelect = false, onpickup = null, onCreatureMove = null, onCreatureAttack = null, onbody = null, onWarpStep = null }: {
@@ -224,11 +224,10 @@
let actorPos = $state<Record<string, { x: number; y: number }>>({});
// --- The fx: each batch plays once, on its own beat. -----------------
// The effect must track ONLY the batch: the server sends `events` then
// `state` back to back, so anything else in the dependency set (me, the
// view) re-runs this before a zero-delay timer can fire — and an eager
// cleanup would cancel every spell before it left the wand. Timers die
// only with the component.
// Track ONLY the batch: the server sends `events` then `state` back to
// back, so a wider dependency set re-runs this within the same frame,
// before a zero-delay timer can fire. No per-run cleanup for the same
// reason — timers die only with the component.
let playedBatch = 0;
const fxTimers = new Set<ReturnType<typeof setTimeout>>();
$effect(() => {
@@ -250,9 +249,8 @@
fxTimers.add(starter);
}
});
return undefined;
});
$effect(() => () => fxTimers.forEach(clearTimeout));
onDestroy(() => fxTimers.forEach(clearTimeout));
// --- Other bodies glide between views. -------------------------------
let prevView: GameView | null = null;
@@ -528,7 +526,6 @@
}
.bezel-top { border-bottom: 1px solid #2a2620; }
.bezel-bottom { border-top: 1px solid #2a2620; }
.bezel-spacer { flex: 1; }
.bezel-zone { flex: 1; display: flex; align-items: center; gap: 8px; }
.bezel-center { justify-content: center; }
.bezel-right { justify-content: flex-end; }