Duels record the rules revision they began under
CURRENT_RULES is stamped on the start line and the game state, and a ledger replays under the revision it recorded (1 when it has none), so a future fix that changes how a past turn resolves can keep the old path for old duels. The reports desk skill now says when to bump it: only when the deploy gate flags ledgers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
8eca078043
commit
7485d50b8d
@@ -179,6 +179,7 @@ function loadSaved(): SavedDuel | null {
|
||||
// Saves at this version may lack fields that came later; the engine expects them present.
|
||||
saved.state.seats ??= Object.keys(saved.state.wizards);
|
||||
saved.state.lessonsShown ??= [];
|
||||
saved.state.rules ??= 1;
|
||||
if (saved.state.lastTurn) {
|
||||
saved.state.lastTurn.events ??= [];
|
||||
saved.state.lastTurn.lessons ??= [];
|
||||
|
||||
+16
-1
@@ -129,7 +129,21 @@ export interface Outcome {
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules revision a duel resolves under. A duel keeps the revision it began
|
||||
* with, so a ledger replays to what its players saw even after the resolver
|
||||
* changes. Bump this only when a fix changes how an already-played turn would
|
||||
* resolve, which the deploy gate (deploy/verify-ledgers.sh) shows by flagging
|
||||
* ledgers; a fix that diverges from no ledger ships without a bump. Keep the
|
||||
* old path behind `state.rules < N` and pin both with tests.
|
||||
*
|
||||
* 1 Bartle's text as first implemented (2026-09-22).
|
||||
*/
|
||||
export const CURRENT_RULES = 1;
|
||||
|
||||
export interface GameState {
|
||||
/** The revision this duel resolves under; see CURRENT_RULES. */
|
||||
rules: number;
|
||||
turn: number;
|
||||
/** Seats in table order; every wizard in play, whether still standing or not. */
|
||||
seats: WizardId[];
|
||||
@@ -235,9 +249,10 @@ function makeWizard(id: WizardId, name: string): WizardState {
|
||||
};
|
||||
}
|
||||
|
||||
export function createGame(names: Record<WizardId, string>, seed = Date.now()): GameState {
|
||||
export function createGame(names: Record<WizardId, string>, seed = Date.now(), rules = CURRENT_RULES): GameState {
|
||||
const seats = Object.keys(names);
|
||||
return {
|
||||
rules,
|
||||
turn: 0,
|
||||
seats,
|
||||
wizards: Object.fromEntries(seats.map((id) => [id, makeWizard(id, names[id])])),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { SPECTATOR, createGame } from './state';
|
||||
import { CURRENT_RULES, SPECTATOR, createGame } from './state';
|
||||
import { viewFor } from './view';
|
||||
|
||||
/** Three wizards; B's first gestures were hidden from C alone. */
|
||||
@@ -30,3 +30,10 @@ describe('the view a room sends', () => {
|
||||
expect(view.log.map((l) => l.text)).toEqual(['Nothing else happened.']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the rules revision', () => {
|
||||
it('is stamped on a new duel and kept from a ledger that recorded one', () => {
|
||||
expect(createGame({ A: 'Aldric', B: 'Bryn' }).rules).toBe(CURRENT_RULES);
|
||||
expect(createGame({ A: 'Aldric', B: 'Bryn' }, 1, 1).rules).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user