The great simplification: every vintage gate collapses, the count restarts at 1

All 50 rooms were retired (backed up on the droplet and in Spaces), so no
ledger needs an old branch to replay: forty revisions of deckRev gates
fold into one canonical ruleset — the newest behavior everywhere. The
ward's arming, the idiot's steering, the lazy illusion roll, the legacy
redirection swap, and the flat wave all leave with their gates; RULES_REV
restarts at 1 for whenever the rules fork again. Old-vintage test pins go
with them. The opening screen now quietly drops seats whose rooms the
server no longer knows, instead of listing them "unreachable" forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-21 12:45:35 -04:00
co-authored by Claude Fable 5
parent e8df6efcde
commit 8a50ff7f36
19 changed files with 383 additions and 685 deletions
@@ -339,8 +339,8 @@ describe("cast modifiers", () => {
target: { kind: "player", playerId: defender },
});
state = must(state, defender, { type: "counteract", instanceId: "reverse#R" });
// REVERSE is a total stop of the harm: the attacker's pass settles it.
state = must(state, attacker, { type: "pass" });
state = must(state, defender, { type: "pass" });
const d = state.players.find((p) => p.id === defender)!;
expect(d.life).toBe(19); // gained 4 instead of losing it
expect(d.lostTurns).toBe(1); // the stun still applies
@@ -349,7 +349,7 @@ describe("cast modifiers", () => {
describe("holding the door open (Pick Lock / Master Key)", () => {
function doorRig() {
let { state } = createGame({ playerIds: ["holder", "guest"], seed: 42, sets: ["basic"], deckRev: 14 });
let { state } = createGame({ playerIds: ["holder", "guest"], seed: 42, sets: ["basic"] });
state = toRound2(state);
// Find a door edge; stand the acting player beside it.
const view = boardView(state);
@@ -410,9 +410,9 @@ describe("holding the door open (Pick Lock / Master Key)", () => {
});
});
describe("a held door is an open doorway to the eye (rules rev 15)", () => {
function sightRig(deckRev: number) {
let { state } = createGame({ playerIds: ["holder", "pursuer"], seed: 42, sets: ["basic"], deckRev });
describe("a held door is an open doorway to the eye", () => {
function sightRig() {
let { state } = createGame({ playerIds: ["holder", "pursuer"], seed: 42, sets: ["basic"] });
state = toRound2(state);
const view = boardView(state);
for (const [key, edge] of Object.entries(view.edges)) {
@@ -431,7 +431,7 @@ describe("a held door is an open doorway to the eye (rules rev 15)", () => {
}
it("rev 15: the holder blasts the pursuer through the doorway", () => {
let { state, cell, side, holder, pursuer } = sightRig(15);
let { state, cell, side, holder, pursuer } = sightRig();
const pick = giveCard(state, holder, "pick-lock");
state = must(state, holder, {
type: "cast", instanceId: pick.instanceId,
@@ -447,7 +447,7 @@ describe("a held door is an open doorway to the eye (rules rev 15)", () => {
});
it("an unheld unlocked door still blocks sight", () => {
let { state, cell, side, holder, pursuer } = sightRig(15);
let { state, cell, side, holder, pursuer } = sightRig();
const pick = giveCard(state, holder, "pick-lock");
state = must(state, holder, {
type: "cast", instanceId: pick.instanceId,
@@ -459,18 +459,4 @@ describe("a held door is an open doorway to the eye (rules rev 15)", () => {
});
expect(refused.ok).toBe(false);
});
it("earlier revisions keep the blocked sightline, hold or no", () => {
let { state, cell, side, holder, pursuer } = sightRig(14);
const pick = giveCard(state, holder, "pick-lock");
state = must(state, holder, {
type: "cast", instanceId: pick.instanceId,
target: { kind: "edge", cell, side }, params: { hold: true },
});
const fb = giveCard(state, holder, "fireball", "FB", 1);
const refused = applyCommand(state, holder, {
type: "cast", instanceId: fb.instanceId, target: { kind: "player", playerId: pursuer },
});
expect(refused.ok).toBe(false);
});
});