1. The spectator-eyes hole.snapshotFor in src/engine/arena.ts fell back to players[0] for a viewer id it couldn't find, handing that connection someone else's position and side-mates. It now returns the same spectator view a seatless connection already gets. Added a test to arena.test.ts that sends an unknown id and asserts the response carries spectator: true and no position — confirmed it fails against the old fallback (returns player a1's data) and passes against the fix.
2. Val's vocabulary, out of the engine.aim.ts's comments now describe "the game's own weapon rules" and "the weapon's own spread" instead of citing weapons.ts and a Vandal. predict.js's refused doc now says "a rule the game itself raises and drops, not a wall on the map" instead of naming the buy-phase spawn pen. predict.test.ts had an uncalled-out val reference ("planted, or stopped to defuse") that I generalized too. Seat.board — the engine's internal cache of the last-sent sticky field — is renamed stickyCache, since board was only ever val's name for what it makes sticky; val's own snapshot field is still called board (that's val's choice, untouched). boundary.test.ts's forbidden-word list now comes from src/words.json, a data file outside src/engine/ that val supplies (containing the same nouns as before); the test reads it via fs, not an import (which would itself trip the "no engine module imports outside" check), and test.skipIf makes the guard a no-op when the file is absent, which is what a freshly copied template gets until it writes its own list. Verified: removing words.json skips cleanly, and a real code-level use of a listed word (not just a comment example) still fails the test.
After this pass, a grep for val-specific terms across src/engine/ and static/engine/ turns up nothing outside comment examples that are either the issue's own illustrative case (in boundary.test.ts, explaining why the list isn't hardcoded) or generic engine vocabulary (round, smoke as an occlusion primitive) that isn't Valorant-specific and wasn't in scope.
bun test: 302 pass, 0 fail (test count up by one, from the new arena test).
Pushed successfully.
Both hardening jobs from #24 are closed:
1. The spectator-eyes hole.
snapshotForinsrc/engine/arena.tsfell back toplayers[0]for a viewer id it couldn't find, handing that connection someone else's position and side-mates. It now returns the same spectator view a seatless connection already gets. Added a test toarena.test.tsthat sends an unknown id and asserts the response carriesspectator: trueand no position — confirmed it fails against the old fallback (returns playera1's data) and passes against the fix.2. Val's vocabulary, out of the engine.
aim.ts's comments now describe "the game's own weapon rules" and "the weapon's own spread" instead of citingweapons.tsand a Vandal.predict.js'srefuseddoc now says "a rule the game itself raises and drops, not a wall on the map" instead of naming the buy-phase spawn pen.predict.test.tshad an uncalled-out val reference ("planted, or stopped to defuse") that I generalized too.Seat.board— the engine's internal cache of the last-sent sticky field — is renamedstickyCache, sinceboardwas only ever val's name for what it makes sticky; val's own snapshot field is still calledboard(that's val's choice, untouched).boundary.test.ts's forbidden-word list now comes fromsrc/words.json, a data file outsidesrc/engine/that val supplies (containing the same nouns as before); the test reads it viafs, not an import (which would itself trip the "no engine module imports outside" check), andtest.skipIfmakes the guard a no-op when the file is absent, which is what a freshly copied template gets until it writes its own list. Verified: removingwords.jsonskips cleanly, and a real code-level use of a listed word (not just a comment example) still fails the test.After this pass, a grep for val-specific terms across
src/engine/andstatic/engine/turns up nothing outside comment examples that are either the issue's own illustrative case (inboundary.test.ts, explaining why the list isn't hardcoded) or generic engine vocabulary (round,smokeas an occlusion primitive) that isn't Valorant-specific and wasn't in scope.bun test: 302 pass, 0 fail (test count up by one, from the new arena test).Closes #30