Credibility pass: sweep the workshop floor

Scoped to everything since the last pass (695307d). The residue of
fast iteration, removed: a reduced-motion media query that had
swallowed a full copy of the .faq-seal rules; doc comments orphaned
from their functions by inserted methods; the FAQ scrape's seams
(section headings run into ruling bodies under the wrong topics, a
next-page heading shipped as a ruling, an amputated "h", and rulings
filed under alphabetically-nearest strangers — Large Rock/Dagger now
lives on those cards; Book of Spells and Torquemada describe no card
in this set and are gone); the write-only aisle flag left behind by
the reverted rev 7; a linter-silenced dead destructure; a duplicated
median lookup; dead casts; and the fallback path that ignored the
apprentice's one-card draw.

Tests now typecheck (tsconfig includes test/), which surfaced the
missing type imports and a drifted creature literal hiding under an
as-cast. Also: a no-op self-assignment, mid-file imports hoisted, a
dynamic-import habit made static, a hedge comment replaced with a
loud setup failure, the fallback-retries-itself dead rung removed
from both bot drivers, and the twin peek scrims merged.

Deliberately kept: the TIERS lookup guard (ledger JSON is untrusted),
the wand-cost stanzas (they differ on the power-attack trade — a
rules question, not a dedup), and rollDie's coexistence with rollD4
(migrating changes visible logs; future work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 20:20:37 -04:00
co-authored by Claude Fable 5
parent 73150a89b5
commit 3308850bb6
18 changed files with 136 additions and 142 deletions
+13 -16
View File
@@ -9,6 +9,10 @@ import {
type SectorPlacement,
} from "../src/board";
import { buildDeck } from "../src/cards";
import { activePlayer, applyCommand, createGame } from "../src/game";
import { createRng } from "../src/rng";
import { setupBoard } from "../src/setups";
import { giveCard } from "./helpers";
describe("card data", () => {
it("basic deck is exactly 125 cards (official 6e rulebook)", () => {
@@ -109,9 +113,7 @@ describe("board assembly", () => {
});
describe("player counts 2-6", () => {
it("assembles a coherent board for every supported count", async () => {
const { setupBoard } = await import("../src/setups");
const { createRng } = await import("../src/rng");
it("assembles a coherent board for every supported count", () => {
for (const n of [2, 3, 4, 5, 6]) {
const { board } = setupBoard(n, createRng(n * 7));
expect(board.homes.length).toBe(n);
@@ -127,8 +129,7 @@ describe("player counts 2-6", () => {
}
});
it("full games start at every count", async () => {
const { createGame } = await import("../src/game");
it("full games start at every count", () => {
for (const n of [3, 5, 6]) {
const ids = Array.from({ length: n }, (_, i) => `w${i}`);
const { state } = createGame({ playerIds: ids, seed: 99 + n, sets: ["basic", "expansion1"] });
@@ -158,12 +159,7 @@ describe("sight and the wraparound openings", () => {
});
});
import { createRng } from "../src/rng";
import { setupBoard as setupBoardForPins } from "../src/setups";
describe("setup diagram pairings (rulebook Set-Up Diagram)", () => {
const setupBoard = setupBoardForPins;
function onEdge(c: { x: number; y: number }, origin: { x: number; y: number }, side: string): boolean {
if (side === "N") return c.y === origin.y && c.x >= origin.x && c.x < origin.x + 5;
if (side === "S") return c.y === origin.y + 4 && c.x >= origin.x && c.x < origin.x + 5;
@@ -203,17 +199,16 @@ describe("setup diagram pairings (rulebook Set-Up Diagram)", () => {
}
});
it("relocation discards the aisle warp: only opposite edges connect after", async () => {
const { createGame, applyCommand, activePlayer } = await import("../src/game");
it("relocation discards the aisle warp: only opposite edges connect after", () => {
let { state } = createGame({ playerIds: ["a", "b", "c"], seed: 7, sets: ["basic"], deckRev: 5 });
const bent = (warps: typeof state.board.warps) =>
warps.filter((w) => w.from.cell.x !== w.to.cell.x && w.from.cell.y !== w.to.cell.y);
expect(bent(state.board.warps).length).toBeGreaterThan(0); // the arc exists
const me = activePlayer(state);
me.hand[0] = { instanceId: "rel#1", cardId: "relocate-sector" };
const rel = giveCard(state, me.id, "relocate-sector");
// Slide the top sector from (5,0) across to (0,0): an L becomes a block.
const r = applyCommand(state, me.id, {
type: "cast", instanceId: "rel#1",
type: "cast", instanceId: rel.instanceId,
target: { kind: "cell", cell: { x: 0, y: 0 } }, params: { cell: { x: 7, y: 2 } },
});
if (!r.ok) throw new Error(r.error);
@@ -223,8 +218,10 @@ describe("setup diagram pairings (rulebook Set-Up Diagram)", () => {
describe("the aisle warp treats its corner as adjacent — sight included", () => {
it("sight passes through the AUTO WARP, diagonals and all", () => {
const { board } = setupBoardForPins(3, createRng(7));
const aisle = board.warps.find((w) => w.aisle)!;
const { board } = setupBoard(3, createRng(7));
const aisle = board.warps.find(
(w) => w.from.cell.x !== w.to.cell.x && w.from.cell.y !== w.to.cell.y,
)!;
expect(aisle).toBeDefined();
// From the aisle mouth, squares beyond the corner are visible — including
// at least one OFF the straight axis (the adjacency promise).