diff --git a/deploy/replay-verify.mjs b/deploy/replay-verify.mjs new file mode 100644 index 0000000..c507b05 --- /dev/null +++ b/deploy/replay-verify.mjs @@ -0,0 +1,21 @@ +// Strict full-ledger replay: ANY refused command is a hard failure. +import { readFileSync } from "fs"; +import { createGame, applyCommand } from "../packages/engine/src/game.ts"; +const lines = readFileSync(process.argv[2], "utf8").trim().split("\n").map((l) => JSON.parse(l)); +const meta = lines.find((l) => l.kind === "meta"); +const start = lines.find((l) => l.kind === "start"); +if (!start) { + console.log(`OK ${process.argv[2].split("/").pop()}: lobby only, nothing to replay`); + process.exit(0); +} +const joins = lines.filter((l) => l.kind === "join"); +const playerIds = [...new Set([meta.hostId, ...joins.map((j) => j.name)])]; +let { state } = createGame({ playerIds, seed: meta.seed, sets: start.expansion ? ["basic", "expansion1"] : ["basic"], colors: start.colors, deckRev: start.deckRev }); +let n = 0; +for (const l of lines) { + if (l.kind !== "command") continue; + const r = applyCommand(state, l.playerId, l.command); + if (!r.ok) { console.log(`FAIL ${process.argv[2].split("/").pop()} seq ${l.seq}: ${r.error}`); process.exit(1); } + state = r.state; n++; +} +console.log(`OK ${process.argv[2].split("/").pop()}: ${n} commands replay clean`); diff --git a/deploy/verify-ledgers.sh b/deploy/verify-ledgers.sh new file mode 100755 index 0000000..821dc9a --- /dev/null +++ b/deploy/verify-ledgers.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Pre-deploy determinism check: fetch every production ledger and strictly +# replay it against the LOCAL engine. Any refused command is a hard failure. +# Run from the repo root before deploying any engine change. +set -euo pipefail +HOST="${1:-104.236.96.198}" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +scp -q "root@$HOST:/var/lib/wizwar/rooms/*.jsonl" "$TMP/" +fails=0 +for f in "$TMP"/*.jsonl; do + out=$(npx tsx deploy/replay-verify.mjs "$f" 2>&1 | tail -1) + case "$out" in + FAIL*) echo "$out"; fails=$((fails+1));; + esac +done +count=$(ls "$TMP" | wc -l | tr -d ' ') +echo "--- $count ledgers checked, $fails failures" +[ "$fails" -eq 0 ]