Credibility pass: one voice, no scars

A three-reviewer sweep for tells of piecemeal machine generation,
every finding verified against the code before touching it. No
behavior changes; the full suite passes unchanged (plus two
strengthened pins).

Engine: removed four void-silenced fossils (a parseEdgeKey call
voided where it stood, stoneEffect's ignored cardId parameter, the
actualTarget remnant in doCast, a voided loop variable in shadow
upkeep); fixed the initialize-then-overwrite narration in
spawnCreature; replaced a filter(() => false) no-op; waterwall now
rides waveFromEdge instead of carrying its own verbatim copy (and the
single-caller washBack wrapper went with it); blind wall-bumps and
LOS blockers each collapsed to one implementation; the wand-id list
and the "permanent" duration sentinel became named constants; the
ambush number local no longer shadows the imported numberValue
function; assorted reviewer-aimed phrasings rewritten as the
constraints they guard.

Server/deploy: the protocol header now documents all eleven message
types; dropped an eslint pragma with no eslint, a test script with no
tests, and an rsync exclude anchored at a path that never existed
(the real data/ dir now excluded); the Caddy vhost has one source of
truth; stale "pending DNS" note removed — the record resolves.

Web: ~90 lines of CSS swallowed verbatim into a mobile media query
deduplicated; the reduced-motion guard on the board now actually
stops the marked-cell pulse; one shared color module replaces two
drifted palettes; an orphaned doc comment rejoined its function.

Tests: the ten-times-pasted helper block became test/helpers.ts;
wave-numbered files renamed for the behaviors they pin; deliberation
comments and void-ed corpses of unwritten assertions deleted; silent
seed-dependent early-returns now fail loudly; one assertion that
compared a value to itself now pins the home-translation it meant to;
the stored-log single-number command form gained the explicit
compatibility test it deserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 11:56:55 -04:00
co-authored by Claude Fable 5
parent f62fcf2510
commit 695307daa8
24 changed files with 240 additions and 716 deletions
+2 -4
View File
@@ -5,8 +5,7 @@
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"typecheck": "tsc --noEmit",
"test": "vitest run"
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@wizwar/engine": "*",
@@ -16,7 +15,6 @@
"@types/node": "^22.0.0",
"@types/ws": "^8.5.0",
"tsx": "^4.19.0",
"typescript": "^5.6.0",
"vitest": "^2.1.0"
"typescript": "^5.6.0"
}
}
+15 -7
View File
@@ -1,14 +1,23 @@
// Websocket front door. Protocol (JSON messages):
// client -> server:
// {type:"create", name} create a room, become host
// {type:"join", roomId, name} join (or rejoin) a room
// {type:"start"} host starts the game
// {type:"join", roomId, name, token?} join, or reclaim a seat by token
// {type:"start", expansion?} host starts the game
// {type:"command", command} a game Command for the engine
// {type:"pickColor", color} lobby standee choice (0-5)
// {type:"makeTransfer"} mint a seat-transfer phrase
// {type:"claimTransfer", code} claim a seat on a new device
// {type:"catchUp", sinceSeq} replay of moves missed while away
// {type:"myGames", seats} summaries for held seats
// {type:"stats"} the engagement tally
// {type:"hotseatReport", ...} anonymous hotseat game counts
// server -> client:
// {type:"welcome"} on connect
// {type:"room", roomId, players, hostId, started}
// {type:"seat", playerId, token} your seat secret — keep it
// {type:"room", roomId, players, hostId, started, colors}
// {type:"events", events} redacted for this recipient
// {type:"state", view} redacted full view (after every change)
// {type:"state", view, seq} redacted full view (after every change)
// {type:"transferCode"|"transferClaimed"|"catchUp"|"games"|"stats"}
// {type:"error", message}
import { createServer } from "node:http";
@@ -47,7 +56,6 @@ const NAME_MAX = 24;
/** Player/room names: printable, trimmed, bounded. */
function cleanName(raw: unknown): string {
// eslint-disable-next-line no-control-regex
return String(raw ?? "").replace(/[\u0000-\u001f\u007f]/g, "").trim().slice(0, NAME_MAX);
}
@@ -109,7 +117,7 @@ const httpServer = createServer((req, res) => {
res.writeHead(500).end();
}
});
const wss = new WebSocketServer({ server: httpServer, path: undefined, maxPayload: 64 * 1024 });
const wss = new WebSocketServer({ server: httpServer, maxPayload: 64 * 1024 });
httpServer.listen(port, host);
interface Session {
@@ -163,7 +171,7 @@ function broadcast(room: Room, makeMessage: (playerId: PlayerId) => unknown): vo
}
function broadcastRoomState(room: Room): void {
broadcast(room, (playerId) => roomInfo(room));
broadcast(room, () => roomInfo(room));
if (room.state) {
broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length }));
}