Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd98f46267 | |||
| c60d465711 | |||
| ce2b29f5df |
@@ -6,6 +6,23 @@ project follows [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.43.3] — 2026-07-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Replays are now self-contained (schema v4).** A replay stores the dealt
|
||||||
|
board itself via the upstream `card_game` session serializers instead of
|
||||||
|
re-dealing from the seed at playback time, so replays survive RNG and
|
||||||
|
upstream upgrades that change the seed→deal mapping — the failure that had
|
||||||
|
silently broken every stored replay. The web player and web game now
|
||||||
|
exchange the full payload through the wasm layer (the old JS path hardcoded
|
||||||
|
`schema_version: 2`, uploaded empty move lists, and corrupted u64 seeds via
|
||||||
|
`Math.round`), and the replay viewer reports unplayable old-format replays
|
||||||
|
in the caption instead of dying silently. Pre-v4 replays are rejected by a
|
||||||
|
version gate; local histories repopulate with new wins. (#170)
|
||||||
|
- **Difficulty-mode wins can upload.** The server's replay `mode` validation
|
||||||
|
now accepts data-carrying `GameMode` variants (previously a 400). (#170)
|
||||||
|
|
||||||
## [0.42.0] — 2026-07-06
|
## [0.42.0] — 2026-07-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ test("draw-mode toggle affects replay payload draw_mode", async ({ page }) => {
|
|||||||
|
|
||||||
const payload = await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload());
|
const payload = await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload());
|
||||||
expect(payload.draw_mode).toBe("DrawThree");
|
expect(payload.draw_mode).toBe("DrawThree");
|
||||||
expect(payload.schema_version).toBe(2);
|
expect(payload.schema_version).toBe(4);
|
||||||
expect(Array.isArray(payload.moves)).toBeTruthy();
|
expect(Array.isArray(payload.recording?.instructions)).toBeTruthy();
|
||||||
expect(payload.moves.length).toBeGreaterThan(0);
|
expect(payload.recording.instructions.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("autonomous play keeps invariants stable across seed batch", async ({ page }) => {
|
test("autonomous play keeps invariants stable across seed batch", async ({ page }) => {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ test("debug failure report contains replay diagnostics", async ({ page }) => {
|
|||||||
expect(report.invariants).toBeTruthy();
|
expect(report.invariants).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("replay payload builder exports schema-v2 moves", async ({ page }) => {
|
test("replay payload builder exports a schema-v4 recording", async ({ page }) => {
|
||||||
await page.goto("/play-classic?seed=42");
|
await page.goto("/play-classic?seed=42");
|
||||||
await page.waitForFunction(() => typeof window.__FERROUS_DEBUG__ === "object");
|
await page.waitForFunction(() => typeof window.__FERROUS_DEBUG__ === "object");
|
||||||
|
|
||||||
@@ -57,10 +57,14 @@ test("replay payload builder exports schema-v2 moves", async ({ page }) => {
|
|||||||
.poll(async () => await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload() !== null))
|
.poll(async () => await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload() !== null))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
const payload = await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload());
|
const payload = await page.evaluate(() => window.__FERROUS_DEBUG__.replayPayload());
|
||||||
expect(payload.schema_version).toBe(2);
|
// Schema v4: the wasm layer assembles the whole payload; the deal is
|
||||||
|
// embedded in `recording` and moves live at recording.instructions.
|
||||||
|
expect(payload.schema_version).toBe(4);
|
||||||
expect(payload.draw_mode).toMatch(/Draw(One|Three)/);
|
expect(payload.draw_mode).toMatch(/Draw(One|Three)/);
|
||||||
expect(payload.mode).toBe("Classic");
|
expect(payload.mode).toBe("Classic");
|
||||||
expect(Array.isArray(payload.moves)).toBeTruthy();
|
expect(payload.recording).toBeTruthy();
|
||||||
expect(payload.moves.length).toBeGreaterThan(0);
|
expect(payload.recording.initial_state).toBeTruthy();
|
||||||
expect(payload.win_move_index).toBe(payload.moves.length - 1);
|
expect(Array.isArray(payload.recording.instructions)).toBeTruthy();
|
||||||
|
expect(payload.recording.instructions.length).toBeGreaterThan(0);
|
||||||
|
expect(payload.win_move_index).toBe(payload.recording.instructions.length - 1);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user