{ "status": "All bindings joined to passing unit/browser runs and matching source hashes; independent Phase6/human gate separate", "records": [ { "tier": "primary", "id": "AC-01-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "room": { "id": "room-a", "capacity": 4, "players": [ "p1" ] }, "actor": "p2" }, "actions": [ { "type": "join-room" } ] }, "assertionGroups": [ { "oraclePaths": [ "attempts.0.path", "attempts.0.authority", "attempts.0.result.membership", "attempts.0.result.roomId", "attempts.1.path", "attempts.1.authority", "attempts.1.result.membership", "attempts.1.result.roomId", "attempts.2.path", "attempts.2.authority", "attempts.2.result.membership", "attempts.2.result.roomId", "attempts.3.path", "attempts.3.authority", "attempts.3.result.membership", "attempts.3.result.roomId" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-02-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "capacities": [ 1, 2, 8, 25, 26 ], "fullRoomPlayers": [ "p1", "p2" ] }, "actions": [ { "type": "configure-and-join-room" } ] }, "assertionGroups": [ { "oraclePaths": [ "capacityResults.0.capacity", "capacityResults.0.accepted", "capacityResults.1.capacity", "capacityResults.1.accepted", "capacityResults.2.capacity", "capacityResults.2.accepted", "capacityResults.3.capacity", "capacityResults.3.accepted", "capacityResults.4.capacity", "capacityResults.4.accepted", "fullJoin.result", "fullJoin.playersAfter.0", "fullJoin.playersAfter.1", "fullJoin.versionAfter" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "fills capacities 2/8/25", "sourceTest": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "line": 129, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "joinRoom", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" } ] }, { "observation": "room", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" }, { "line": 136, "expression": "expect(room).toEqual(before)" }, { "line": 136, "expression": "expect(room)" } ] }, { "observation": "calls", "statements": [ { "line": 154, "expression": "expect(calls).toEqual([[1, capacity]])" }, { "line": 154, "expression": "expect(calls)" } ] }, { "observation": "match.anchorId", "statements": [ { "line": 155, "expression": "expect(match.anchorId).toBe(roster[index - 1])" }, { "line": 155, "expression": "expect(match.anchorId)" } ] } ], "executions": [ { "title": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/rooms.test.ts", "title": "enforces the 2–25 capacity range", "sourceTest": "enforces the 2–25 capacity range, private default and fixed Crockford code format", "line": 6, "sourceHash": "e139ee023ce51c1a9dc6c8bc38cfc4cb89aff8f5c9761b2ffb6b803b4a4053b3", "assertedObservations": [ { "observation": "roomCapacity", "statements": [ { "line": 7, "expression": "expect([1,2,25,26,2.5,'2',NaN].map(roomCapacity)).toEqual([false,true,true,false,false,false,false])" }, { "line": 7, "expression": "expect([1,2,25,26,2.5,'2',NaN].map(roomCapacity))" } ] } ], "executions": [ { "title": "AC-01/02/24–28/50/51 room policy and private projections enforces the 2–25 capacity range, private default and fixed Crockford code format", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-02-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "capacity": 2, "players": [ "p1" ], "commands": [ "join-2", "join-3" ] }, "actions": [ { "type": "claim-final-seat-concurrently" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.commandId", "results.0.result", "results.1.commandId", "results.1.result", "playersAfter.0", "playersAfter.1", "membershipsForFinalSeat" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-03-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "profileId": "p1", "selectedIcon": "owl" }, "actions": [ { "type": "select-profile-icon" } ] }, "assertionGroups": [ { "oraclePaths": [ "persistedIcon", "projections.room", "projections.scorecard", "projections.summary", "projections.standings" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Choose frog through actual preferences, reload, assert aria-pressed, then the selected icon in room roster, live scorecard, revealed round summary and final standings after eight actual rounds.", "bindings": [ { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "chosen icon persists", "sourceTest": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "line": 281, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "aria-pressed", "statements": [ { "line": 296, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 298, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" } ] }, { "observation": "frog", "statements": [ { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] }, { "observation": "final", "statements": [ { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/identity.ts", "src/web/identity-panel.tsx", "src/web/room-screen.tsx", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-04-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "roster": [ "p9", "p3", "p7" ], "randomReturns": [ 1, 3 ] }, "actions": [ { "type": "select-first-anchor" } ] }, "assertionGroups": [ { "oraclePaths": [ "randomCalls.0.min", "randomCalls.0.max", "randomCalls.1.min", "randomCalls.1.max", "selections.0.randomValue", "selections.0.anchorId", "selections.1.randomValue", "selections.1.anchorId" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-04-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-04-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-04-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "anchorId": "p2", "categoryId": "animals" }, "actions": [ { "type": "choose-first-category" } ] }, "assertionGroups": [ { "oraclePaths": [ "attempts.0.actorId", "attempts.0.result", "attempts.0.matchVersion", "attempts.1.actorId", "attempts.1.result", "attempts.1.matchVersion", "categoryId" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-04-T02 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-04-T02 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-05-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "categories": [ { "id": "animals", "background": "forest.svg" }, { "id": "space", "background": "stars.svg" } ], "pairs": [ { "id": "a1", "category": "animals" }, { "id": "a2", "category": "animals" }, { "id": "s1", "category": "space" } ] }, "actions": [ { "type": "render-category-rounds" } ] }, "assertionGroups": [ { "oraclePaths": [ "rounds.0.pairId", "rounds.0.background", "rounds.0.controls.readable", "rounds.1.pairId", "rounds.1.background", "rounds.1.controls.readable", "rounds.2.pairId", "rounds.2.background", "rounds.2.controls.readable", "missingAsset.background", "missingAsset.label" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Every seed pair matches its category asset; two different heroes pairs load heroes.svg and colors loads colors.svg in Chromium. Readable foreground/overlay contrast and enabled controls are measured. Forced missing art shows an accessible unavailable status and usable controls; explicit retry can recover.", "bindings": [ { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "counterpart artwork failure", "sourceTest": "AC-05-T01 counterpart artwork failure shows an accessible fallback with usable answer controls", "line": 60, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "data-artwork", "statements": [ { "line": 75, "expression": "expect(page.locator(\".game-panel\")).toHaveAttribute(\n \"data-artwork\",\n \"fallback\",\n )" }, { "line": 92, "expression": "expect(page.locator(\".game-panel\")).toHaveAttribute(\n \"data-artwork\",\n \"loaded\",\n )" } ] }, { "observation": "Your answer", "statements": [ { "line": 74, "expression": "expect(page.getByLabel(\"Your answer\", { exact: true })).toBeEnabled()" }, { "line": 74, "expression": "expect(page.getByLabel(\"Your answer\", { exact: true }))" }, { "line": 99, "expression": "expect(page.getByLabel(\"Your answer\", { exact: true })).toBeEnabled()" }, { "line": 99, "expression": "expect(page.getByLabel(\"Your answer\", { exact: true }))" } ] }, { "observation": "Category artwork unavailable", "statements": [ { "line": 71, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n ).toBeVisible()" }, { "line": 71, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n )" }, { "line": 96, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n ).toHaveCount(0)" }, { "line": 96, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-05-T01 counterpart artwork failure shows an accessible fallback with usable answer controls", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "counterpart real", "sourceTest": "`AC-05-T01 counterpart real ${category} asset for pair ${index}`", "line": 110, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "observed.background", "statements": [ { "line": 132, "expression": "expect(observed.background).toContain(`/backgrounds/${category}.svg`)" }, { "line": 132, "expression": "expect(observed.background)" } ] }, { "observation": "pair.prompt", "statements": [ { "line": 124, "expression": "expect(page.locator(\".game-prompt\")).toHaveText(pair.prompt)" } ] }, { "observation": "data-artwork", "statements": [ { "line": 120, "expression": "expect(page.locator(\".game-panel\")).toHaveAttribute(\n \"data-artwork\",\n \"loaded\",\n )" } ] } ], "executions": [ { "title": "AC-05-T01 counterpart real heroes asset for pair 0", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-05-T01 counterpart real heroes asset for pair 1", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-05-T01 counterpart real colors asset for pair 0", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "artwork retry renders the successfully probed URL", "sourceTest": "AC-05-T01 counterpart artwork retry renders the successfully probed URL", "line": 5, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "observed.decoded", "statements": [ { "line": 50, "expression": "expect(observed.decoded).toBe(true)" }, { "line": 50, "expression": "expect(observed.decoded)" } ] }, { "observation": "observed.background", "statements": [ { "line": 51, "expression": "expect(observed.background).toContain(\"/backgrounds/heroes.svg?attempt=1\")" }, { "line": 51, "expression": "expect(observed.background)" } ] }, { "observation": "Category artwork unavailable", "statements": [ { "line": 22, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n ).toBeVisible()" }, { "line": 22, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n )" }, { "line": 52, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n ).toHaveCount(0)" }, { "line": 52, "expression": "expect(\n page.getByText(\"Category artwork unavailable\", { exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-05-T01 counterpart artwork retry renders the successfully probed URL", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/application/catalog-seed.test.ts", "title": "ships six categories", "sourceTest": "ships six categories with ten distinct directed complete pairs, three static hints and one shared background each", "line": 7, "sourceHash": "74fe5ad464f763fbea257818146eb9de80d709f05ddef26ba91206ee6a399532", "assertedObservations": [ { "observation": "backgroundId", "statements": [ { "line": 9, "expression": "expect(pairs.every(p=>p.backgroundId===category.backgroundId)).toBe(true)" }, { "line": 9, "expression": "expect(pairs.every(p=>p.backgroundId===category.backgroundId))" }, { "line": 9, "expression": "expect(existsSync(`public/backgrounds/${category.backgroundId}.svg`)).toBe(true)" }, { "line": 9, "expression": "expect(existsSync(`public/backgrounds/${category.backgroundId}.svg`))" } ] } ], "executions": [ { "title": "ships six categories with ten distinct directed complete pairs, three static hints and one shared background each", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/web/game-panel.tsx", "src/infrastructure/catalog-seed.json", "public/backgrounds/heroes.svg", "public/backgrounds/colors.svg" ], "fixtureDiscrepancy": [ "Historical animals/forest.svg fixtures are replaced by actual approved category assets. Actual CSS background URLs, successful image loads and fallback observations are retained." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-06-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "stage": "no-hint", "acceptedAnswer": "cat", "submissions": [ "cat", "dog", "cat" ] }, "actions": [ { "type": "submit-answer" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.answer", "results.0.accepted", "results.0.scoreDelta", "results.1.answer", "results.1.accepted", "results.1.scoreDelta", "results.2.answer", "results.2.accepted", "results.2.scoreDelta", "scoreAfter" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-06-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-06-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-07-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "acceptedAnswer": "cat", "stages": [ "hint-1", "hint-2", "hint-3" ] }, "actions": [ { "type": "submit-answer-at-hint-stages" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.stage", "results.0.scoreDelta", "results.1.stage", "results.1.scoreDelta", "results.2.stage", "results.2.scoreDelta" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-07-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-07-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-08-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "playerId": "p1", "target": "cat" }, "actions": [ { "type": "exhaust-hints" } ] }, "assertionGroups": [ { "oraclePaths": [ "playerResult.scoreDelta", "playerResult.solved", "projectionAfterHint3.revealedTarget" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-08-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-08-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-09-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "stage": "hint-1", "unsolved": [ "p1", "p2" ] }, "actions": [ { "type": "submit-stage-answers" } ] }, "assertionGroups": [ { "oraclePaths": [ "transitions.0.submitted.0", "transitions.0.stage", "transitions.0.hintRecipients", "transitions.1.submitted.0", "transitions.1.submitted.1", "transitions.1.stage", "transitions.1.hintRecipients.0", "transitions.1.hintRecipients.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-09-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-09-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-10-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "playerId": "p1", "icon": "owl", "scoreBefore": 4, "stage": "hint-1", "roundStartedAtMs": 1000, "serverAcceptedAtMs": 13000, "clientClaimedAtMs": 2000 }, "actions": [ { "type": "submit-answer", "payload": { "answer": "cat" } }, { "type": "project-live-scorecard" } ] }, "assertionGroups": [ { "oraclePaths": [ "acceptedSubmission.playerId", "acceptedSubmission.acceptedAtMs", "acceptedSubmission.elapsedMs", "acceptedSubmission.scoreDelta", "scorecard.0.playerId", "scorecard.0.icon", "scorecard.0.roundPoints", "scorecard.0.cumulativePoints", "scorecard.0.elapsedMs" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-10-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-10-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "chosen icon persists", "sourceTest": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "line": 281, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "aria-pressed", "statements": [ { "line": 296, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 298, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" } ] }, { "observation": "frog", "statements": [ { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] }, { "observation": "final", "statements": [ { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output.", "Icon in the direct driver comes from roomProjection; the selected-icon browser case separately observes actual scorecard rendering. Server elapsed/current/cumulative values are actual accepted commands." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-11-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "submissions": [ { "playerId": "p2", "acceptedAtMs": 200, "sequence": 2 }, { "playerId": "p1", "acceptedAtMs": 100, "sequence": 1 }, { "playerId": "p3", "acceptedAtMs": 100, "sequence": 2 } ] }, "actions": [ { "type": "accept-competing-correct-answers" } ] }, "assertionGroups": [ { "oraclePaths": [ "chooserId", "orderedCandidates.0", "orderedCandidates.1", "orderedCandidates.2" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-11-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-11-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-12-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "priorScores": { "p1": 5, "p2": 3 }, "roundResults": { "p1": { "points": 0, "elapsedMs": null }, "p2": { "points": 3, "elapsedMs": 12000 } } }, "actions": [ { "type": "project-round-summary" } ] }, "assertionGroups": [ { "oraclePaths": [ "rows.0.playerId", "rows.0.cumulativeScore", "rows.0.roundPoints", "rows.0.elapsedMs", "rows.1.playerId", "rows.1.cumulativeScore", "rows.1.roundPoints", "rows.1.elapsedMs" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-12-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-12-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output.", "Historical unsolved elapsed=null is derived from no solve. Actual standings independently apply the 80000ms unsolved penalty." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-13-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "configuredRounds": 3, "completedRound": 3, "totals": { "p1": 10, "p2": 12 } }, "actions": [ { "type": "project-final-standings" } ] }, "assertionGroups": [ { "oraclePaths": [ "status", "rows.0.rank", "rows.0.playerId", "rows.0.totalScore", "rows.1.rank", "rows.1.playerId", "rows.1.totalScore" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/eight-round-counterpart.test.ts", "title": "actual commands finish only after eight", "sourceTest": "AC-13-T01 counterpart: actual commands finish only after eight rounds and satisfy the unchanged final ordering oracle", "line": 7, "sourceHash": "f880097075f4fc6fcd7ac9ee41184c9d043bec455849a9ca5c3ada69e217cd59", "assertedObservations": [ { "observation": "store.state.status", "statements": [ { "line": 42, "expression": "expect(h.store.state.status).not.toBe(\"finished\")" }, { "line": 42, "expression": "expect(h.store.state.status)" }, { "line": 45, "expression": "expect(h.store.state.status).toBe(\"finished\")" }, { "line": 45, "expression": "expect(h.store.state.status)" } ] }, { "observation": "store.state.rounds", "statements": [ { "line": 44, "expression": "expect(h.store.state.rounds).toHaveLength(8)" }, { "line": 44, "expression": "expect(h.store.state.rounds)" } ] }, { "observation": "scenario.assertObservation", "statements": [ { "line": 49, "expression": "scenario.assertObservation({\n status: view.status === \"finished\" ? \"final\" : view.status,\n rows: view.standings.map((r) => ({\n rank: r.rank,\n playerId: r.profileId,\n totalScore: r.points,\n })),\n })" } ] } ], "executions": [ { "title": "AC-13-T01 counterpart: actual commands finish only after eight rounds and satisfy the unchanged final ordering oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output.", "Original configuredRounds=3 fixture remains untouched. Actual commands prove unfinished after rounds 3 and 7 and finished after 8; original final-row ordering oracle is reused." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-14-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "rows": [ { "playerId": "fast", "score": 10, "solveSeconds": [ 10, 20 ], "unsolvedRounds": 0 }, { "playerId": "slow", "score": 10, "solveSeconds": [ 20, 30 ], "unsolvedRounds": 0 }, { "playerId": "penalty-a", "score": 8, "solveSeconds": [ 5 ], "unsolvedRounds": 1 }, { "playerId": "penalty-b", "score": 8, "solveSeconds": [ 5 ], "unsolvedRounds": 1 } ] }, "actions": [ { "type": "rank-final-standings" } ] }, "assertionGroups": [ { "oraclePaths": [ "standings.0.rank", "standings.0.playerId", "standings.0.score", "standings.0.tieSeconds", "standings.1.rank", "standings.1.playerId", "standings.1.score", "standings.1.tieSeconds", "standings.2.rank", "standings.2.playerId", "standings.2.score", "standings.2.tieSeconds", "standings.3.rank", "standings.3.playerId", "standings.3.score", "standings.3.tieSeconds" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-14-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-14-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/match.test.ts", "title": "draws no-solver next category", "sourceTest": "draws no-solver next category uniformly from six categories and preserves exact final ties", "line": 63, "sourceHash": "bed875c0ab14556abc472428b15c16df6120dde5143e62fe17e8caa7d028f970", "assertedObservations": [ { "observation": "standings", "statements": [ { "line": 66, "expression": "expect(standings(tied).map(p=>p.profileId)).toEqual(['p2','p1'])" }, { "line": 66, "expression": "expect(standings(tied).map(p=>p.profileId))" }, { "line": 66, "expression": "expect(standings(tied).map(p=>p.rank)).toEqual([1,2])" }, { "line": 66, "expression": "expect(standings(tied).map(p=>p.rank))" } ] }, { "observation": "rank", "statements": [ { "line": 65, "expression": "expect(rows.map(p=>p.rank)).toEqual([1,1])" }, { "line": 65, "expression": "expect(rows.map(p=>p.rank))" }, { "line": 66, "expression": "expect(standings(tied).map(p=>p.rank)).toEqual([1,2])" }, { "line": 66, "expression": "expect(standings(tied).map(p=>p.rank))" } ] } ], "executions": [ { "title": "Phase4B authoritative rounds, scoring and private views draws no-solver next category uniformly from six categories and preserves exact final ties", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/matches.phase4.spec.ts", "title": "eight authoritative no-solver rounds", "sourceTest": "eight authoritative no-solver rounds show uniform category continuation, reveal and shared final ranking on a phone", "line": 57, "sourceHash": "120a89d6dfd3e782eeb2bfc527a041d762056ff0348f800a775c04088962de63", "assertedObservations": [ { "observation": "Final ranking", "statements": [ { "line": 64, "expression": "expect(page.getByRole('table',{name:'Final ranking'}).getByRole('cell',{name:'1',exact:true})).toHaveCount(2)" }, { "line": 64, "expression": "expect(page.getByRole('table',{name:'Final ranking'}).getByRole('cell',{name:'1',exact:true}))" } ] }, { "observation": "toHaveCount(2)", "statements": [ { "line": 64, "expression": "expect(page.getByRole('table',{name:'Final ranking'}).getByRole('cell',{name:'1',exact:true})).toHaveCount(2)" } ] } ], "executions": [ { "title": "eight authoritative no-solver rounds show uniform category continuation, reveal and shared final ranking on a phone", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output.", "Original aggregated-score fixture is isolated standings arithmetic, not valid per-round awards. Valid per-round scoring, time, unsolved penalty and exact-tie lifecycle counterparts remain separately cited." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-15-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "viewports": [ { "width": 390, "height": 844 }, { "width": 844, "height": 390 } ] }, "actions": [ { "type": "render-phone-game" } ] }, "assertionGroups": [ { "oraclePaths": [ "renders.0.orientation", "renders.0.scrollWidth", "renders.0.clientWidth", "renders.0.visible.0", "renders.0.visible.1", "renders.0.visible.2", "renders.0.interactions.0", "renders.0.interactions.1", "renders.1.orientation", "renders.1.scrollWidth", "renders.1.clientWidth", "renders.1.visible.0", "renders.1.visible.1", "renders.1.visible.2", "renders.1.interactions.0", "renders.1.interactions.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "At 320×568, 568×320, 390×844 and 844×390, answer/hints/scorecard are visible by normal vertical scroll, no horizontal overflow occurs, keyboard Tab/Enter and touch submit actual answers; 25-player/25-observer long roster also renders on 320-pixel phone.", "bindings": [ { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "keyboard and touch", "sourceTest": "`AC-15/44 counterparts ${viewport.width}x${viewport.height} keyboard and touch`", "line": 149, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "scrollWidth", "statements": [ { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n ).toBe(true)" }, { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n )" } ] }, { "observation": "sample.ratios", "statements": [ { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n ).toBeGreaterThanOrEqual(4.5)" }, { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n )" } ] }, { "observation": "measured.focus", "statements": [ { "line": 242, "expression": "expect(measured.focus.style).toBe(\"solid\")" }, { "line": 242, "expression": "expect(measured.focus.style)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width)).toBeGreaterThanOrEqual(3)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width))" } ] }, { "observation": "measured.buttonHeight", "statements": [ { "line": 244, "expression": "expect(measured.buttonHeight).toBeGreaterThanOrEqual(44)" }, { "line": 244, "expression": "expect(measured.buttonHeight)" } ] }, { "observation": "Solved", "statements": [ { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n ).toBeVisible()" }, { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n )" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/)).toBeVisible()" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/))" } ] } ], "executions": [ { "title": "AC-15/44 counterparts 320x568 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 568x320 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 390x844 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 844x390 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/app/globals.css", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-16-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "records": [ "complete-approved", "missing-background", "disabled", "unapproved" ] }, "actions": [ { "type": "query-playable-catalog" } ] }, "assertionGroups": [ { "oraclePaths": [ "playableIds.0", "excludedIds.0", "excludedIds.1", "excludedIds.2" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Complete approved records are selectable. Missing prompt, target, ordered hint, policy field or background rejects the catalog; missing category/unapproved records are excluded; disabled/selectable=false pairs are excluded from future pools while historical versions remain resolvable. Canonical target with zero alternatives is valid.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "rejects each incomplete catalog field", "sourceTest": "AC-16 primary/supplemental counterpart rejects each incomplete catalog field and excludes unapproved or disabled future content", "line": 96, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "buildCatalog", "statements": [ { "line": 106, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, ...broken }] as never).ok,\n ).toBe(false)" }, { "line": 106, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, ...broken }] as never).ok,\n )" }, { "line": 109, "expression": "expect(\n value(buildCatalog(catalog.categories, [{ ...pair, categoryId: \"\" }]))\n .pairs,\n ).toEqual([])" }, { "line": 109, "expression": "expect(\n value(buildCatalog(catalog.categories, [{ ...pair, categoryId: \"\" }]))\n .pairs,\n )" }, { "line": 124, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, acceptedAnswers: [] }]).ok,\n ).toBe(true)" }, { "line": 124, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, acceptedAnswers: [] }]).ok,\n )" } ] }, { "observation": "categoryPool", "statements": [ { "line": 120, "expression": "expect(categoryPool(filtered, pair.categoryId).map((p) => p.id)).toEqual([\n pair.id,\n ])" }, { "line": 120, "expression": "expect(categoryPool(filtered, pair.categoryId).map((p) => p.id))" } ] } ], "executions": [ { "title": "AC-16 primary/supplemental counterpart rejects each incomplete catalog field and excludes unapproved or disabled future content", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/catalog.ts", "src/domain/content.ts" ], "fixtureDiscrepancy": [ "Historical acceptedAnswers=[] meant missing policy; production target is canonical and acceptedAnswers lists alternatives, so [] is valid, while undefined/malformed policy is invalid. Historical enabled=false maps to selectable=false. Malformed complete catalog input is rejected as a whole, preventing that record from entering play." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-17-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "attempts": [ { "playerId": "p1", "solveStage": "no-hint" }, { "playerId": "p2", "solveStage": "hint-1" } ] }, "actions": [ { "type": "nominate-pair" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.playerId", "results.0.result", "results.1.playerId", "results.1.result" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/application/content.test.ts", "title": "normalizes candidates", "sourceTest": "AC-17/18 normalizes candidates, permits only current revealed no-hint solves and prevents duplicate profile/candidate or eligible-round use", "line": 12, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "nominate", "statements": [ { "line": 13, "expression": "expect(nominate(state,{...m,status:'playing',rounds:[{...m.rounds[0],revealed:false}]},player('p1'),'Future word',1,ids).ok).toBe(false)" }, { "line": 13, "expression": "expect(nominate(state,{...m,status:'playing',rounds:[{...m.rounds[0],revealed:false}]},player('p1'),'Future word',1,ids).ok)" }, { "line": 13, "expression": "expect(nominate(state,resultMatch('p1',1),player('p1'),'Future word',1,ids).ok).toBe(false)" }, { "line": 13, "expression": "expect(nominate(state,resultMatch('p1',1),player('p1'),'Future word',1,ids).ok)" }, { "line": 14, "expression": "expect(nominate(state,m,player('p1'),'future word',1,ids).ok).toBe(false)" }, { "line": 14, "expression": "expect(nominate(state,m,player('p1'),'future word',1,ids).ok)" }, { "line": 14, "expression": "expect(nominate(state,m,player('p1'),'another',1,ids).ok).toBe(false)" }, { "line": 14, "expression": "expect(nominate(state,m,player('p1'),'another',1,ids).ok)" }, { "line": 14, "expression": "expect(nominate(state,m,player('observer'),'third',1,ids).ok).toBe(false)" }, { "line": 14, "expression": "expect(nominate(state,m,player('observer'),'third',1,ids).ok)" }, { "line": 15, "expression": "expect(nominate(emptyContent(),m,player('p1'),word,1,ids).ok).toBe(false)" }, { "line": 15, "expression": "expect(nominate(emptyContent(),m,player('p1'),word,1,ids).ok)" } ] } ], "executions": [ { "title": "AC-17/18 normalizes candidates, permits only current revealed no-hint solves and prevents duplicate profile/candidate or eligible-round use", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-18-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "candidates": [ { "id": "n1", "nominations": 9, "moderation": "approved", "curated": true }, { "id": "n2", "nominations": 10, "moderation": "pending", "curated": true }, { "id": "n3", "nominations": 10, "moderation": "approved", "curated": false } ] }, "actions": [ { "type": "evaluate-nomination" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.id", "results.0.eligibleForCuration", "results.0.playable", "results.1.id", "results.1.eligibleForCuration", "results.1.playable", "results.2.id", "results.2.eligibleForCuration", "results.2.playable" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/application/content.test.ts", "title": "counts unique profiles", "sourceTest": "AC-18/47 counts unique profiles at 9/10, denies unauthorized/self review and approval alone never publishes", "line": 17, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "status", "statements": [ { "line": 18, "expression": "expect(state.cases[0].status).toBe('collecting')" }, { "line": 18, "expression": "expect(state.cases[0].status)" }, { "line": 19, "expression": "expect(state.cases[0].status).toBe('pending')" }, { "line": 19, "expression": "expect(state.cases[0].status)" }, { "line": 20, "expression": "expect(state.cases[0]).toMatchObject({status:'approved',playable:false})" } ] }, { "observation": "reviewCase", "statements": [ { "line": 18, "expression": "expect(reviewCase(state,id,mod,'approve','Reason',2).ok).toBe(false)" }, { "line": 18, "expression": "expect(reviewCase(state,id,mod,'approve','Reason',2).ok)" }, { "line": 19, "expression": "expect(reviewCase(state,id,player('p9'),'approve','Reason',2).ok).toBe(false)" }, { "line": 19, "expression": "expect(reviewCase(state,id,player('p9'),'approve','Reason',2).ok)" }, { "line": 19, "expression": "expect(reviewCase(state,id,{profileId:'p0',roles:['moderator']},'approve','Reason',2).ok).toBe(false)" }, { "line": 19, "expression": "expect(reviewCase(state,id,{profileId:'p0',roles:['moderator']},'approve','Reason',2).ok)" }, { "line": 19, "expression": "expect(reviewCase(state,id,mod,'approve','',2).ok).toBe(false)" }, { "line": 19, "expression": "expect(reviewCase(state,id,mod,'approve','',2).ok)" } ] }, { "observation": "futureEligible", "statements": [ { "line": 20, "expression": "expect(contentProjection(state,player('p9')).cases[0].futureEligible).toBe(true)" }, { "line": 20, "expression": "expect(contentProjection(state,player('p9')).cases[0].futureEligible)" } ] }, { "observation": "publications", "statements": [ { "line": 20, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 20, "expression": "expect(state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 counts unique profiles at 9/10, denies unauthorized/self review and approval alone never publishes", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "activation requires an approved case", "sourceTest": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "line": 28, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "activateCase", "statements": [ { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 29, "expression": "expect(state.publications).toHaveLength(1)" }, { "line": 29, "expression": "expect(state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 29, "expression": "expect(state.cases[0].playable).toBe(true)" }, { "line": 29, "expression": "expect(state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-18-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "caseId": "n1", "proposerId": "p1", "originalReviewerId": "m1", "seniorModeratorId": "m2", "rejectedVersion": 4 }, "actions": [ { "type": "appeal", "payload": { "actorId": "p1", "commandId": "appeal-1", "reason": "Context supplied" } }, { "type": "appeal", "payload": { "actorId": "p1", "commandId": "appeal-1", "reason": "Context supplied" } }, { "type": "resolve-appeal", "payload": { "actorId": "m2", "commandId": "resolve-1", "reason": "Evidence verified", "outcome": "overturn" } } ] }, "assertionGroups": [ { "oraclePaths": [ "commandResults.0.commandId", "commandResults.0.result", "commandResults.0.version", "commandResults.1.commandId", "commandResults.1.result", "commandResults.1.version", "commandResults.2.commandId", "commandResults.2.result", "commandResults.2.version", "audit.0.actorId", "audit.0.action", "audit.0.reason", "audit.1.actorId", "audit.1.action", "audit.1.reason", "directPublications", "originalReviewerResolution", "secondAppeal", "staleVersionResult" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/application/content.test.ts", "title": "allows one proposer appeal", "sourceTest": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "line": 22, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "resolveAppeal", "statements": [ { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok)" } ] }, { "observation": "appealCase", "statements": [ { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 23, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 23, "expression": "expect(state.publications)" } ] }, { "observation": "status", "statements": [ { "line": 23, "expression": "expect(state.cases[0].status).toBe(outcome==='uphold'?'rejected':'approved')" }, { "line": 23, "expression": "expect(state.cases[0].status)" } ] } ], "executions": [ { "title": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content-service.test.ts", "title": "commits audited suggestions once", "sourceTest": "AC-19/47 commits audited suggestions once, retains canonical retry, rejects stale and rolls back failed storage", "line": 9, "sourceHash": "fcaa556df70b721e9c8c3c40fdb20ed3c0a6644b63ea086ff27d04ff92085d07", "assertedObservations": [ { "observation": "service.act", "statements": [ { "line": 13, "expression": "expect(await service.act(command,action)).toEqual(result)" }, { "line": 13, "expression": "expect(await service.act(command,action))" }, { "line": 13, "expression": "expect(await service.act({...command,commandId:'stale'},action)).toEqual(failure('CONFLICT'))" }, { "line": 13, "expression": "expect(await service.act({...command,commandId:'stale'},action))" }, { "line": 14, "expression": "expect(await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).toEqual(failure('TEMPORARY_UNAVAILABLE'))" }, { "line": 14, "expression": "expect(await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'}))" }, { "line": 14, "expression": "expect((await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).ok).toBe(true)" }, { "line": 14, "expression": "expect((await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).ok)" } ] }, { "observation": "audits", "statements": [ { "line": 13, "expression": "expect(audits).toHaveLength(1)" }, { "line": 13, "expression": "expect(audits)" }, { "line": 14, "expression": "expect(audits).toHaveLength(1)" }, { "line": 14, "expression": "expect(audits)" }, { "line": 14, "expression": "expect(audits).toHaveLength(2)" }, { "line": 14, "expression": "expect(audits)" } ] }, { "observation": "state.publications", "statements": [ { "line": 14, "expression": "expect(state.publications).toEqual([])" }, { "line": 14, "expression": "expect(state.publications)" } ] } ], "executions": [ { "title": "AC-19/47 commits audited suggestions once, retains canonical retry, rejects stale and rolls back failed storage", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-19-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "attempts": [ { "wins": 9, "pairs": 3 }, { "wins": 10, "pairs": 3 }, { "wins": 10, "pairs": 2 } ] }, "actions": [ { "type": "submit-category-suggestion" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0", "results.1", "results.2", "openSuggestions" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/application/content.test.ts", "title": "requires ten lifetime wins", "sourceTest": "AC-19 requires ten lifetime wins, exactly three distinct pairs and only one open suggestion", "line": 25, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "suggestCategory", "statements": [ { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),9,proposal,1,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),9,proposal,1,ids,catalog).ok)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,{...proposal,pairs:proposal.pairs.slice(0,2)},1,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,{...proposal,pairs:proposal.pairs.slice(0,2)},1,ids,catalog).ok)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,proposal,2,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,proposal,2,ids,catalog).ok)" } ] }, { "observation": "playable", "statements": [ { "line": 26, "expression": "expect(state.cases[0].playable).toBe(false)" }, { "line": 26, "expression": "expect(state.cases[0].playable)" } ] }, { "observation": "publications", "statements": [ { "line": 26, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 26, "expression": "expect(state.publications)" } ] } ], "executions": [ { "title": "AC-19 requires ten lifetime wins, exactly three distinct pairs and only one open suggestion", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "activation requires an approved case", "sourceTest": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "line": 28, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "activateCase", "statements": [ { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 29, "expression": "expect(state.publications).toHaveLength(1)" }, { "line": 29, "expression": "expect(state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 29, "expression": "expect(state.cases[0].playable).toBe(true)" }, { "line": 29, "expression": "expect(state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-20-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "callbacks": [ { "token": "verified" }, { "token": "bad-signature" } ] }, "actions": [ { "type": "complete-oidc-callback" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.token", "results.0.profileId", "results.0.associationsCreated", "results.1.token", "results.1.error", "results.1.associationsCreated" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual identity application handles generated guest opt-out, stable browser credential and stable verified issuer/subject member association without guest linking. Browser IndexedDB/cookie clear resets the guest; reload retains preferences; uncertain preference retry has identical payload and one mutation; stale shared-session mutation refreshes identity. Actual OIDC adapter rejects invalid signature, issuer, audience, state, nonce and expiry; invalid callbacks never associate a profile. Private API derives actor and rejects forged displayed identity/roles.", "bindings": [ { "file": "tests/browser/recovery.phase6.spec.ts", "title": "guest clear/reset", "sourceTest": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "line": 164, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "state.guests", "statements": [ { "line": 215, "expression": "expect(state.guests.size).toBe(2)" }, { "line": 215, "expression": "expect(state.guests.size)" } ] }, { "observation": "state.members", "statements": [ { "line": 213, "expression": "expect(state.members.size).toBe(1)" }, { "line": 213, "expression": "expect(state.members.size)" }, { "line": 214, "expression": "expect([...state.members.values()][0]).toBe(member)" }, { "line": 214, "expression": "expect([...state.members.values()][0])" } ] }, { "observation": "state.profiles", "statements": [ { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits).toHaveLength(1)" }, { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits)" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon).toBe(\"owl\")" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon)" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon).toBe(\"owl\")" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon)" } ] }, { "observation": "preferenceBodies", "statements": [ { "line": 178, "expression": "expect(state.preferenceBodies[0]).toEqual(state.preferenceBodies[1])" }, { "line": 178, "expression": "expect(state.preferenceBodies[0])" } ] }, { "observation": "aria-pressed", "statements": [ { "line": 175, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 181, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 198, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose owl\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" } ] } ], "executions": [ { "title": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/application/identity.test.ts", "title": "passes only verified identities", "sourceTest": "passes only verified identities to profile association and refuses invalid browser credentials", "line": 12, "sourceHash": "365b8b6405d3ada55c5216848a115b41a5f14be485f6fb80fa2697f969b5240c", "assertedObservations": [ { "observation": "service", "statements": [ { "line": 14, "expression": "expect(await service.guest('untrusted')).toEqual(failure('VALIDATION_ERROR'))" }, { "line": 14, "expression": "expect(await service.guest('untrusted'))" }, { "line": 16, "expression": "expect(await service.completeLogin('b'.repeat(43),'code','state')).toEqual(failure('NOT_AUTHENTICATED'))" }, { "line": 16, "expression": "expect(await service.completeLogin('b'.repeat(43),'code','state'))" } ] }, { "observation": "member", "statements": [ { "line": 18, "expression": "expect(repository.member).not.toHaveBeenCalled()" }, { "line": 18, "expression": "expect(repository.member)" } ] } ], "executions": [ { "title": "AC-20/21/46 identity application boundaries passes only verified identities to profile association and refuses invalid browser credentials", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/integration/oidc.test.ts", "title": "rejects a malformed or untrusted token", "sourceTest": "rejects a malformed or untrusted token without returning provider details: %j", "line": 51, "sourceHash": "9e5aaa26111e31e1601254465bb4a3541b0bcb80981735bd7c34014a442964b5", "assertedObservations": [ { "observation": "verify", "statements": [ { "line": 54, "expression": "expect(await provider(options).instance.verify(callback)).toEqual(failure('NOT_AUTHENTICATED'))" }, { "line": 54, "expression": "expect(await provider(options).instance.verify(callback))" } ] } ], "executions": [ { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"wrongSignature\":true}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"iss\":\"https://wrong.example.test\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"aud\":\"wrong-audience\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"nonce\":\"wrong-nonce\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"exp\":1}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"sub\":\"\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"nbf\":1793057668}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"tokenStatus\":400}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/identity.ts", "src/infrastructure/oidc.ts", "src/web/identity-panel.tsx", "src/web/guest-credential.ts" ], "fixtureDiscrepancy": [ "Provider discovery/JWKS/token transport and persistence are local fakes; no live account or provider entitlement is claimed. Browser member entry invokes application begin/completeLogin behind this verified-claims port." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-21-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "guestIcon": "owl", "roomCode": "ABCD" }, "actions": [ { "type": "enter-as-guest" } ] }, "assertionGroups": [ { "oraclePaths": [ "identity.kind", "identity.id", "identity.icon", "allowedActions.0", "allowedActions.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual identity application handles generated guest opt-out, stable browser credential and stable verified issuer/subject member association without guest linking. Browser IndexedDB/cookie clear resets the guest; reload retains preferences; uncertain preference retry has identical payload and one mutation; stale shared-session mutation refreshes identity. Actual OIDC adapter rejects invalid signature, issuer, audience, state, nonce and expiry; invalid callbacks never associate a profile. Private API derives actor and rejects forged displayed identity/roles.", "bindings": [ { "file": "tests/browser/recovery.phase6.spec.ts", "title": "guest clear/reset", "sourceTest": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "line": 164, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "state.guests", "statements": [ { "line": 215, "expression": "expect(state.guests.size).toBe(2)" }, { "line": 215, "expression": "expect(state.guests.size)" } ] }, { "observation": "state.members", "statements": [ { "line": 213, "expression": "expect(state.members.size).toBe(1)" }, { "line": 213, "expression": "expect(state.members.size)" }, { "line": 214, "expression": "expect([...state.members.values()][0]).toBe(member)" }, { "line": 214, "expression": "expect([...state.members.values()][0])" } ] }, { "observation": "state.profiles", "statements": [ { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits).toHaveLength(1)" }, { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits)" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon).toBe(\"owl\")" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon)" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon).toBe(\"owl\")" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon)" } ] }, { "observation": "preferenceBodies", "statements": [ { "line": 178, "expression": "expect(state.preferenceBodies[0]).toEqual(state.preferenceBodies[1])" }, { "line": 178, "expression": "expect(state.preferenceBodies[0])" } ] }, { "observation": "aria-pressed", "statements": [ { "line": 175, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 181, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 198, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose owl\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" } ] } ], "executions": [ { "title": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/recovery.phase6.spec.ts", "title": "empty and lost-seat quick match", "sourceTest": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "line": 320, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "rooms.size", "statements": [ { "line": 432, "expression": "expect(rooms.size).toBe(1)" }, { "line": 432, "expression": "expect(rooms.size)" } ] }, { "observation": "1 / 8 players", "statements": [ { "line": 427, "expression": "expect(\n page.getByText(\"1 / 8 players · 7 open player slots · waiting\"),\n ).toBeVisible()" }, { "line": 427, "expression": "expect(\n page.getByText(\"1 / 8 players · 7 open player slots · waiting\"),\n )" } ] } ], "executions": [ { "title": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/identity.ts", "src/infrastructure/oidc.ts", "src/web/identity-panel.tsx", "src/web/guest-credential.ts" ], "fixtureDiscrepancy": [ "Provider discovery/JWKS/token transport and persistence are local fakes; no live account or provider entitlement is claimed. Browser member entry invokes application begin/completeLogin behind this verified-claims port." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-22-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "rooms": [ { "id": "open", "discoverable": true, "state": "waiting", "playerCount": 1, "capacity": 4 }, { "id": "hidden", "discoverable": false, "state": "waiting", "playerCount": 1, "capacity": 4 }, { "id": "full", "discoverable": true, "state": "waiting", "playerCount": 4, "capacity": 4 }, { "id": "started", "discoverable": true, "state": "playing", "playerCount": 1, "capacity": 4 } ] }, "actions": [ { "type": "browse-lobbies" } ] }, "assertionGroups": [ { "oraclePaths": [ "joinableRoomIds.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/application/public-projections.test.ts", "title": "shows only discoverable waiting", "sourceTest": "AC-22/43 shows only discoverable waiting lobbies with free seats, and stable oldest-first order", "line": 129, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "publicLobbies", "statements": [ { "line": 137, "expression": "expect(publicLobbies(rooms, 60000).map((entry) => entry.roomId)).toEqual([\n \"a\",\n \"b\",\n ])" }, { "line": 137, "expression": "expect(publicLobbies(rooms, 60000).map((entry) => entry.roomId))" }, { "line": 141, "expression": "expect(\n publicLobbies([room(\"future\", { createdAt: 90000 })], 60000)[0]\n .ageSeconds,\n ).toBe(0)" }, { "line": 141, "expression": "expect(\n publicLobbies([room(\"future\", { createdAt: 90000 })], 60000)[0]\n .ageSeconds,\n )" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections AC-22/43 shows only discoverable waiting lobbies with free seats, and stable oldest-first order", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-23-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "eligibleRooms": [ { "id": "room-b", "createdAt": 100 }, { "id": "room-a", "createdAt": 100 }, { "id": "room-c", "createdAt": 200 } ] }, "actions": [ { "type": "quick-match" } ] }, "assertionGroups": [ { "oraclePaths": [ "selectedRoomId", "joinCommand.authority", "emptyFallback.0", "emptyFallback.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "oldest-room port contract", "sourceTest": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "line": 22, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "selected", "statements": [ { "line": 89, "expression": "expect(selected).toEqual([\"z\", \"a\", \"a\"])" }, { "line": 89, "expression": "expect(selected)" } ] }, { "observation": "service.quickMatch", "statements": [ { "line": 78, "expression": "expect(await service.quickMatch(\"p2\", \"first\", \"first\")).toEqual(\n success({ joined: \"z\", fallback: [] }),\n )" }, { "line": 78, "expression": "expect(await service.quickMatch(\"p2\", \"first\", \"first\"))" }, { "line": 90, "expression": "expect(await service.quickMatch(\"p4\", \"retry\", \"retry\")).toEqual(\n success({ joined: \"b\", fallback: [] }),\n )" }, { "line": 90, "expression": "expect(await service.quickMatch(\"p4\", \"retry\", \"retry\"))" }, { "line": 93, "expression": "expect(await service.quickMatch(\"p5\", \"empty\", \"empty\")).toEqual(\n success({ joined: null, fallback: [\"browse\", \"create\"] }),\n )" }, { "line": 93, "expression": "expect(await service.quickMatch(\"p5\", \"empty\", \"empty\"))" } ] }, { "observation": "stores.every", "statements": [ { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2)).toBe(true)" }, { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2))" } ] } ], "executions": [ { "title": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/recovery.phase6.spec.ts", "title": "empty and lost-seat quick match", "sourceTest": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "line": 320, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "No joinable rooms", "statements": [ { "line": 410, "expression": "expect(\n page.getByText(\n \"No joinable rooms right now. Browse the community board or create a room.\",\n ),\n ).toBeVisible()" }, { "line": 410, "expression": "expect(\n page.getByText(\n \"No joinable rooms right now. Browse the community board or create a room.\",\n ),\n )" } ] }, { "observation": "The room or account changed", "statements": [ { "line": 419, "expression": "expect(\n page.getByText(\n \"The room or account changed. Try again, browse, or create a room.\",\n ),\n ).toBeVisible()" }, { "line": 419, "expression": "expect(\n page.getByText(\n \"The room or account changed. Try again, browse, or create a room.\",\n ),\n )" } ] }, { "observation": "Browse open rooms", "statements": [ { "line": 415, "expression": "expect(\n page.getByRole(\"link\", { name: \"Browse open rooms\" }),\n ).toBeVisible()" }, { "line": 415, "expression": "expect(\n page.getByRole(\"link\", { name: \"Browse open rooms\" }),\n )" } ] } ], "executions": [ { "title": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-23-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "roomId": "room-a", "capacity": 2, "players": [ "p1" ], "actors": [ "p2", "p3" ] }, "actions": [ { "type": "quick-match-concurrently" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0", "results.1", "playersAfter.0", "playersAfter.1", "duplicateMemberships" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "oldest-room port contract", "sourceTest": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "line": 22, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "raced", "statements": [ { "line": 85, "expression": "expect(raced.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 85, "expression": "expect(raced.filter((r) => r.ok))" }, { "line": 86, "expression": "expect(\n raced.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 86, "expression": "expect(\n raced.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "stores.every", "statements": [ { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2)).toBe(true)" }, { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2))" } ] }, { "observation": "p3", "statements": [ { "line": 97, "expression": "expect(\n stores.flatMap((s) => s.state.players).filter((p) => p.profileId === \"p3\"),\n ).toHaveLength(1)" }, { "line": 97, "expression": "expect(\n stores.flatMap((s) => s.state.players).filter((p) => p.profileId === \"p3\"),\n )" } ] } ], "executions": [ { "title": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-24-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "requested": [ 1, 2, 25, 26 ] }, "actions": [ { "type": "configure-capacity" } ] }, "assertionGroups": [ { "oraclePaths": [ "decisions.0.value", "decisions.0.accepted", "decisions.1.value", "decisions.1.accepted", "decisions.2.value", "decisions.2.accepted", "decisions.3.value", "decisions.3.accepted" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "fills capacities 2/8/25", "sourceTest": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "line": 129, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "joinRoom", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" } ] }, { "observation": "room", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" }, { "line": 136, "expression": "expect(room).toEqual(before)" }, { "line": 136, "expression": "expect(room)" } ] }, { "observation": "calls", "statements": [ { "line": 154, "expression": "expect(calls).toEqual([[1, capacity]])" }, { "line": 154, "expression": "expect(calls)" } ] }, { "observation": "match.anchorId", "statements": [ { "line": 155, "expression": "expect(match.anchorId).toBe(roster[index - 1])" }, { "line": 155, "expression": "expect(match.anchorId)" } ] } ], "executions": [ { "title": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/rooms.test.ts", "title": "enforces the 2–25 capacity range", "sourceTest": "enforces the 2–25 capacity range, private default and fixed Crockford code format", "line": 6, "sourceHash": "e139ee023ce51c1a9dc6c8bc38cfc4cb89aff8f5c9761b2ffb6b803b4a4053b3", "assertedObservations": [ { "observation": "roomCapacity", "statements": [ { "line": 7, "expression": "expect([1,2,25,26,2.5,'2',NaN].map(roomCapacity)).toEqual([false,true,true,false,false,false,false])" }, { "line": 7, "expression": "expect([1,2,25,26,2.5,'2',NaN].map(roomCapacity))" } ] } ], "executions": [ { "title": "AC-01/02/24–28/50/51 room policy and private projections enforces the 2–25 capacity range, private default and fixed Crockford code format", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-25-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "invitations": [ { "code": "ABCD", "roomId": "active-open" }, { "code": "EFGH", "roomId": "active-full" } ] }, "actions": [ { "type": "follow-room-invitation" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.code", "results.0.resolvedRoomId", "results.0.membership", "results.1.code", "results.1.resolvedRoomId", "results.1.membership" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-26-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "links": [ { "roomId": "open" }, { "roomId": "full" } ] }, "actions": [ { "type": "follow-room-deep-link" } ] }, "assertionGroups": [ { "oraclePaths": [ "browseShown", "results.0.roomId", "results.0.offer", "results.1.roomId", "results.1.offer" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/rooms.phase2.spec.ts", "title": "signed full-room link", "sourceTest": "AC-26/27 signed full-room link bypasses browsing and keeps the inviter binding through observer entry", "line": 5, "sourceHash": "0c857d31bf7f4b246af2deb2639fc0a3066233e9071c007864d3ce3aef72a2bd", "assertedObservations": [ { "observation": "landing", "statements": [ { "line": 22, "expression": "expect(landing).toEqual([])" }, { "line": 22, "expression": "expect(landing)" } ] }, { "observation": "joins", "statements": [ { "line": 21, "expression": "expect(joins).toHaveLength(1)" }, { "line": 21, "expression": "expect(joins)" }, { "line": 21, "expression": "expect(joins[0]).toMatchObject({action:'join',mode:'observer',inviterId:'p2',expectedProfileId:'observer-1',expectedVersion:1})" }, { "line": 21, "expression": "expect(joins[0])" } ] }, { "observation": "Join as player", "statements": [ { "line": 16, "expression": "expect(page.getByRole('button',{name:'Join as player'})).toHaveCount(0)" }, { "line": 16, "expression": "expect(page.getByRole('button',{name:'Join as player'}))" } ] }, { "observation": "Read-only observer", "statements": [ { "line": 20, "expression": "expect(page.getByText(/Read-only observer · Following Player Elm/)).toBeVisible()" }, { "line": 20, "expression": "expect(page.getByText(/Read-only observer · Following Player Elm/))" } ] } ], "executions": [ { "title": "AC-26/27 signed full-room link bypasses browsing and keeps the inviter binding through observer entry", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-27-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "room": { "capacity": 2, "players": [ "p1", "p2" ] }, "invitation": { "observerId": "o1", "inviterId": "p2" } }, "actions": [ { "type": "accept-observer-invite" } ] }, "assertionGroups": [ { "oraclePaths": [ "playerCount", "observers.0.id", "observers.0.follows" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/application/rooms.test.ts", "title": "rejects a full player join", "sourceTest": "rejects a full player join without mutating the state and binds a separately admitted observer to an active inviter", "line": 12, "sourceHash": "e139ee023ce51c1a9dc6c8bc38cfc4cb89aff8f5c9761b2ffb6b803b4a4053b3", "assertedObservations": [ { "observation": "players", "statements": [ { "line": 19, "expression": "expect(observed.value.players).toHaveLength(2)" }, { "line": 19, "expression": "expect(observed.value.players)" } ] }, { "observation": "observed.value.observers", "statements": [ { "line": 19, "expression": "expect(observed.value.observers[0]).toMatchObject({profileId:'o1',followedPlayerId:'p2',arrivalSequence:0})" }, { "line": 19, "expression": "expect(observed.value.observers[0])" } ] }, { "observation": "playerAuthority", "statements": [ { "line": 20, "expression": "expect(playerAuthority(observed.value,'o1')).toEqual({ok:false,error:{code:'NOT_AUTHORIZED'}})" }, { "line": 20, "expression": "expect(playerAuthority(observed.value,'o1'))" }, { "line": 21, "expression": "expect(playerAuthority(observed.value,'p2').ok).toBe(true)" }, { "line": 21, "expression": "expect(playerAuthority(observed.value,'p2').ok)" } ] } ], "executions": [ { "title": "AC-01/02/24–28/50/51 room policy and private projections rejects a full player join without mutating the state and binds a separately admitted observer to an active inviter", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-28-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "observer": { "id": "o1", "follows": "p2" }, "attemptedCommands": [ "answer", "choose-category", "ready" ] }, "actions": [ { "type": "act-as-observer" } ] }, "assertionGroups": [ { "oraclePaths": [ "projectedPlayerId", "commandResults.0", "commandResults.1", "commandResults.2", "scoreRows" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "rejects observers and outsiders", "sourceTest": "rejects observers and outsiders changing readiness or player presence", "line": 46, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "setReady", "statements": [ { "line": 48, "expression": "expect(setReady(room,actor,true,0)).toEqual(failure('NOT_AUTHORIZED'))" }, { "line": 48, "expression": "expect(setReady(room,actor,true,0))" } ] }, { "observation": "presence", "statements": [ { "line": 48, "expression": "expect(presence(room,actor,true,0)).toEqual(failure('NOT_AUTHORIZED'))" }, { "line": 48, "expression": "expect(presence(room,actor,true,0))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 rejects observers and outsiders changing readiness or player presence", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "maximum 25 players", "sourceTest": "AC-02/24/27/28/51 maximum 25 players plus 25 observers keep capacity, locked roster, scoring and perspectives under competing commands", "line": 15, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "observer", "statements": [ { "line": 24, "expression": "expect(joinRoom(room, \"overflow\", \"observer\", \"p1\", 0).ok).toBe(false)" }, { "line": 24, "expression": "expect(joinRoom(room, \"overflow\", \"observer\", \"p1\", 0).ok)" }, { "line": 34, "expression": "expect(room.observers).toHaveLength(25)" }, { "line": 34, "expression": "expect(room.observers)" }, { "line": 67, "expression": "expect(observer).toEqual(player)" }, { "line": 67, "expression": "expect(observer)" }, { "line": 68, "expression": "expect(observer.scorecard).toHaveLength(25)" }, { "line": 68, "expression": "expect(observer.scorecard)" }, { "line": 69, "expression": "expect(observer.standings.every((s) => s.points === 5)).toBe(true)" }, { "line": 69, "expression": "expect(observer.standings.every((s) => s.points === 5))" } ] }, { "observation": "h.act", "statements": [ { "line": 37, "expression": "expect(await h.act({ type: \"choose\", categoryId: \"animals\" }, \"o1\")).toEqual({\n ok: false,\n error: { code: \"NOT_AUTHORIZED\" },\n })" }, { "line": 37, "expression": "expect(await h.act({ type: \"choose\", categoryId: \"animals\" }, \"o1\"))" }, { "line": 70, "expression": "expect((await h.act(action, `o${i}`)).ok).toBe(false)" }, { "line": 70, "expression": "expect((await h.act(action, `o${i}`)).ok)" } ] }, { "observation": "scorecard", "statements": [ { "line": 68, "expression": "expect(observer.scorecard).toHaveLength(25)" }, { "line": 68, "expression": "expect(observer.scorecard)" } ] } ], "executions": [ { "title": "AC-02/24/27/28/51 maximum 25 players plus 25 observers keep capacity, locked roster, scoring and perspectives under competing commands", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/web/lobby-api.test.ts", "title": "returns canonical observer denial", "sourceTest": "returns canonical observer denial and delivers catch-up notifications even when late action is rejected", "line": 21, "sourceHash": "c874ff43ba8727fe72f6c52170d281e4bd2f60b31738fce979e8b408b64dd775", "assertedObservations": [ { "observation": "status).toBe(403)", "statements": [ { "line": 22, "expression": "expect((await POST(request(input),context)).status).toBe(403)" } ] } ], "executions": [ { "title": "returns canonical observer denial and delivers catch-up notifications even when late action is rejected", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-29-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "actorId": "p1", "emoji": "👍", "audience": [ "p1", "p2", "o1" ], "before": { "score": 5, "answer": null, "roundVersion": 3 } }, "actions": [ { "type": "react-with-emoji" } ] }, "assertionGroups": [ { "oraclePaths": [ "event.type", "event.emoji", "event.recipients.0", "event.recipients.1", "event.recipients.2", "after.score", "after.answer", "after.roundVersion" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Exact 👍 ❤️ 😂 🎉 👀 menu; allowed reaction visible to room players/observers, invalid emoji denied without broadcast. Separate reaction aggregate leaves match, room, answer and score unchanged. Canonical resend does not extend lifetime or repeat event. Rate/expiry timing remains separately identified TR-19 evidence.", "bindings": [ { "file": "tests/application/reactions.test.ts", "title": "permits exactly five emoji", "sourceTest": "AC-29/TR-19 permits exactly five emoji and all player/observer recipients without game mutation", "line": 51, "sourceHash": "ca3a244f02112804193820f9a2c8adc824e672bac92eab6dc6ece90abb15d9cf", "assertedObservations": [ { "observation": "REACTION", "statements": [ { "line": 52, "expression": "expect(REACTIONS).toEqual([\"👍\", \"❤️\", \"😂\", \"🎉\", \"👀\"])" }, { "line": 52, "expression": "expect(REACTIONS)" } ] }, { "observation": "room", "statements": [ { "line": 73, "expression": "expect({ room, match }).toEqual(before)" }, { "line": 73, "expression": "expect({ room, match })" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n )" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n )" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n ).toBe(false)" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n )" } ] }, { "observation": "match", "statements": [ { "line": 73, "expression": "expect({ room, match }).toEqual(before)" }, { "line": 73, "expression": "expect({ room, match })" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n )" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n )" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n ).toBe(false)" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n )" } ] } ], "executions": [ { "title": "AC-29/TR-19 permits exactly five emoji and all player/observer recipients without game mutation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/reactions.ts", "src/application/reactions.ts", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-30-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "phase": "waiting", "players": [ "p1", "p2", "p3", "p4" ], "observers": [ { "id": "o2", "arrival": 20 }, { "id": "o1", "arrival": 10 } ], "leavingPlayerId": "p2" }, "actions": [ { "type": "leave-player", "payload": { "playerId": "p2" } } ] }, "assertionGroups": [ { "oraclePaths": [ "players.0", "players.1", "players.2", "players.3", "observers.0", "promoted.observerId", "promoted.position" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "compacts survivors", "sourceTest": "compacts survivors and appends earliest observer, using stable ID for equal arrival times", "line": 35, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "next.players", "statements": [ { "line": 38, "expression": "expect(next.players.map(p=>p.profileId)).toEqual(['p1','p3','p4','o2'])" }, { "line": 38, "expression": "expect(next.players.map(p=>p.profileId))" }, { "line": 39, "expression": "expect(next.players[3].ready).toBe(false)" }, { "line": 39, "expression": "expect(next.players[3].ready)" } ] }, { "observation": "next.observers", "statements": [ { "line": 39, "expression": "expect(next.observers.map(p=>p.profileId)).toEqual(['o9'])" }, { "line": 39, "expression": "expect(next.observers.map(p=>p.profileId))" } ] }, { "observation": "ready", "statements": [ { "line": 39, "expression": "expect(next.players[3].ready).toBe(false)" }, { "line": 39, "expression": "expect(next.players[3].ready)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 compacts survivors and appends earliest observer, using stable ID for equal arrival times", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-30-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "players": [ "p1", "p2", "p3" ], "observers": [], "leaving": "p2" }, "actions": [ { "type": "leave-before-countdown" } ] }, "assertionGroups": [ { "oraclePaths": [ "players.0", "players.1", "observers", "openSeats" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "does not promote if room was not full", "sourceTest": "does not promote if room was not full, and creator has no departure privilege", "line": 42, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "players", "statements": [ { "line": 44, "expression": "expect(next.players.map(p=>p.profileId)).toEqual(['p2','p3'])" }, { "line": 44, "expression": "expect(next.players.map(p=>p.profileId))" } ] }, { "observation": "observers", "statements": [ { "line": 44, "expression": "expect(next.observers).toHaveLength(1)" }, { "line": 44, "expression": "expect(next.observers)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 does not promote if room was not full, and creator has no departure privilege", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-30-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "initialVersion": 7, "staleObserver": "o1", "latestObservers": [ "o2", "o1" ] }, "actions": [ { "type": "resolve-leave-observer-race" } ] }, "assertionGroups": [ { "oraclePaths": [ "firstAttempt", "retriedVersion", "promotedObserver", "players.0", "players.1", "players.2" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "stale departure loses to queue change", "sourceTest": "AC-30-T03 stale departure loses to queue change and retries against the latest oldest observer", "line": 217, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "results", "statements": [ { "line": 247, "expression": "expect(results[0].ok).toBe(true)" }, { "line": 247, "expression": "expect(results[0].ok)" }, { "line": 248, "expression": "expect(results[1]).toEqual(failure(\"CONFLICT\"))" }, { "line": 248, "expression": "expect(results[1])" } ] }, { "observation": "store.state.players", "statements": [ { "line": 249, "expression": "expect(store.state.players.map((p) => p.profileId)).toEqual([\n \"p1\",\n \"p2\",\n \"p3\",\n ])" }, { "line": 249, "expression": "expect(store.state.players.map((p) => p.profileId))" }, { "line": 258, "expression": "expect(store.state.players.map((p) => p.profileId)).toEqual([\n \"p1\",\n \"p3\",\n \"o2\",\n ])" }, { "line": 258, "expression": "expect(store.state.players.map((p) => p.profileId))" }, { "line": 264, "expression": "expect(store.state.players.some((p) => p.profileId === \"o1\")).toBe(false)" }, { "line": 264, "expression": "expect(store.state.players.some((p) => p.profileId === \"o1\"))" } ] }, { "observation": "store.state.observers", "statements": [ { "line": 254, "expression": "expect(store.state.observers.map((o) => o.profileId)).toEqual([\"o2\"])" }, { "line": 254, "expression": "expect(store.state.observers.map((o) => o.profileId))" }, { "line": 263, "expression": "expect(store.state.observers).toEqual([])" }, { "line": 263, "expression": "expect(store.state.observers)" } ] } ], "executions": [ { "title": "AC-30-T03 stale departure loses to queue change and retries against the latest oldest observer", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-30-T04", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "observers": [ { "id": "o9", "arrival": 100 }, { "id": "o2", "arrival": 100 } ] }, "actions": [ { "type": "promote-equal-arrival-observer" } ] }, "assertionGroups": [ { "oraclePaths": [ "promotedObserver", "remainingObservers.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "compacts survivors", "sourceTest": "compacts survivors and appends earliest observer, using stable ID for equal arrival times", "line": 35, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "next.players", "statements": [ { "line": 38, "expression": "expect(next.players.map(p=>p.profileId)).toEqual(['p1','p3','p4','o2'])" }, { "line": 38, "expression": "expect(next.players.map(p=>p.profileId))" }, { "line": 39, "expression": "expect(next.players[3].ready).toBe(false)" }, { "line": 39, "expression": "expect(next.players[3].ready)" } ] }, { "observation": "next.observers", "statements": [ { "line": 39, "expression": "expect(next.observers.map(p=>p.profileId)).toEqual(['o9'])" }, { "line": 39, "expression": "expect(next.observers.map(p=>p.profileId))" } ] }, { "observation": "ready", "statements": [ { "line": 39, "expression": "expect(next.players[3].ready).toBe(false)" }, { "line": 39, "expression": "expect(next.players[3].ready)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 compacts survivors and appends earliest observer, using stable ID for equal arrival times", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-31-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "roomVersion": 4, "players": [ { "id": "p1", "ready": true }, { "id": "p2", "ready": false } ] }, "actions": [ { "type": "mark-final-player-ready" } ] }, "assertionGroups": [ { "oraclePaths": [ "roomVersion", "players.0.id", "players.0.ready", "players.1.id", "players.1.ready", "countdown.seconds", "countdown.roster.0", "countdown.roster.1", "countdown.locked", "emittedEvents.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "starts a five-second warning", "sourceTest": "starts a five-second warning only at all-but-one, then locks immediately on final ready", "line": 51, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "room.status", "statements": [ { "line": 55, "expression": "expect(room.status).toBe('countdown')" }, { "line": 55, "expression": "expect(room.status)" } ] }, { "observation": "countdownDueAt", "statements": [ { "line": 55, "expression": "expect(room.countdownDueAt).toBe(15199)" }, { "line": 55, "expression": "expect(room.countdownDueAt)" } ] }, { "observation": "lockedRoster", "statements": [ { "line": 55, "expression": "expect(room.lockedRoster).toEqual(['p1','p2','p3'])" }, { "line": 55, "expression": "expect(room.lockedRoster)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 starts a five-second warning only at all-but-one, then locks immediately on final ready", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "commits one canonical ready", "sourceTest": "commits one canonical ready result/event and rejects a stale concurrent command without repeating it", "line": 104, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "events", "statements": [ { "line": 107, "expression": "expect(h.events).toHaveLength(1)" }, { "line": 107, "expression": "expect(h.events)" }, { "line": 109, "expression": "expect(h.events).toHaveLength(2)" }, { "line": 109, "expression": "expect(h.events)" } ] }, { "observation": "service.act", "statements": [ { "line": 107, "expression": "expect(await h.service.act(command,{type:'ready',ready:true})).toEqual(accepted)" }, { "line": 107, "expression": "expect(await h.service.act(command,{type:'ready',ready:true}))" }, { "line": 108, "expression": "expect(await h.service.act(competing,{type:'ready',ready:true})).toEqual(failure('CONFLICT'))" }, { "line": 108, "expression": "expect(await h.service.act(competing,{type:'ready',ready:true}))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 commits one canonical ready result/event and rejects a stale concurrent command without repeating it", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-31-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "lockedRoster": [ "p1", "p2", "p3" ], "observers": [ "o1" ], "leaving": "p2" }, "actions": [ { "type": "leave-after-countdown-lock" } ] }, "assertionGroups": [ { "oraclePaths": [ "players.0", "players.1", "observers.0", "vacancy", "promoted" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "never substitutes after lock", "sourceTest": "never substitutes after lock and closes if fewer than two original players remain at expiry", "line": 69, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "room.players", "statements": [ { "line": 71, "expression": "expect(room.players.map(p=>p.profileId)).toEqual(['p2'])" }, { "line": 71, "expression": "expect(room.players.map(p=>p.profileId))" } ] }, { "observation": "room.observers", "statements": [ { "line": 71, "expression": "expect(room.observers[0].followedPlayerId).toBe('p1')" }, { "line": 71, "expression": "expect(room.observers[0].followedPlayerId)" } ] }, { "observation": "next.status", "statements": [ { "line": 72, "expression": "expect(next.status).toBe('closed')" }, { "line": 72, "expression": "expect(next.status)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 never substitutes after lock and closes if fewer than two original players remain at expiry", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-31-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "lockedRoster": [ "p1", "p2" ], "commands": [ "leave-p2", "promote-o1" ] }, "actions": [ { "type": "race-promotion-after-lock" } ] }, "assertionGroups": [ { "oraclePaths": [ "leaveResult", "promotionResult", "players.0", "observers.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "concurrent leave and observer arrival", "sourceTest": "AC-30-T03/31-T03 concurrent leave and observer arrival conflict, recompute promotion, and never substitute after roster lock", "line": 267, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "results", "statements": [ { "line": 329, "expression": "expect(results[0].ok).toBe(true)" }, { "line": 329, "expression": "expect(results[0].ok)" }, { "line": 330, "expression": "expect(results[1]).toEqual(failure(\"CONFLICT\"))" }, { "line": 330, "expression": "expect(results[1])" } ] }, { "observation": "store.state.players", "statements": [ { "line": 310, "expression": "expect(store.state.players.map((p) => p.profileId)).toEqual([\n \"p1\",\n \"p3\",\n \"o1\",\n ])" }, { "line": 310, "expression": "expect(store.state.players.map((p) => p.profileId))" }, { "line": 334, "expression": "expect(store.state.players.map((p) => p.profileId)).toEqual([\"p1\", \"o1\"])" }, { "line": 334, "expression": "expect(store.state.players.map((p) => p.profileId))" } ] }, { "observation": "store.state.lockedRoster", "statements": [ { "line": 335, "expression": "expect(store.state.lockedRoster).toEqual(locked)" }, { "line": 335, "expression": "expect(store.state.lockedRoster)" } ] }, { "observation": "store.state.observers", "statements": [ { "line": 316, "expression": "expect(store.state.observers.map((o) => o.profileId)).toEqual([\"o2\"])" }, { "line": 316, "expression": "expect(store.state.observers.map((o) => o.profileId))" }, { "line": 336, "expression": "expect(store.state.observers.map((o) => o.profileId)).toEqual([\"o2\"])" }, { "line": 336, "expression": "expect(store.state.observers.map((o) => o.profileId))" } ] } ], "executions": [ { "title": "AC-30-T03/31-T03 concurrent leave and observer arrival conflict, recompute promotion, and never substitute after roster lock", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-31-T04", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "lockedRoster": [ "p1", "p2" ], "remainingPlayers": [ "p1" ], "observers": [ "o1" ] }, "actions": [ { "type": "expire-countdown" } ] }, "assertionGroups": [ { "oraclePaths": [ "gameStarted", "roomState", "players.0", "observers.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "never substitutes after lock", "sourceTest": "never substitutes after lock and closes if fewer than two original players remain at expiry", "line": 69, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "next.status", "statements": [ { "line": 72, "expression": "expect(next.status).toBe('closed')" }, { "line": 72, "expression": "expect(next.status)" } ] }, { "observation": "next.closedAt", "statements": [ { "line": 72, "expression": "expect(next.closedAt).toBe(10001)" }, { "line": 72, "expression": "expect(next.closedAt)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 never substitutes after lock and closes if fewer than two original players remain at expiry", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-32-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "warning": { "playerId": "p2", "dueAtMs": 5000 }, "readyAtMs": 4999 }, "actions": [ { "type": "mark-holdout-ready" } ] }, "assertionGroups": [ { "oraclePaths": [ "warningShownForMs", "deadlineResult", "countdown.started", "countdown.seconds" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/browser/lobby.phase3.spec.ts", "title": "two actual clients ready", "sourceTest": "two actual clients ready, lock their roster and close below two without promoting the observer", "line": 35, "sourceHash": "3b6019fb68b5e4595fdd5218e4c84e0cbc2a6b3fee7519d51ce21790cb7f4306", "assertedObservations": [ { "observation": "Mark ready within 5 seconds", "statements": [ { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'})).toContainText('Mark ready within 5 seconds.')" } ] }, { "observation": "getByRole('alert')", "statements": [ { "line": 43, "expression": "expect(b.getByRole('alert').filter({hasText:'The room will wait for both players.'})).toBeVisible()" }, { "line": 43, "expression": "expect(b.getByRole('alert').filter({hasText:'The room will wait for both players.'}))" }, { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'})).toContainText('Mark ready within 5 seconds.')" }, { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'}))" }, { "line": 44, "expression": "expect(a.getByRole('alert').filter({hasText:'Mark ready within'})).toHaveCount(0)" }, { "line": 44, "expression": "expect(a.getByRole('alert').filter({hasText:'Mark ready within'}))" }, { "line": 44, "expression": "expect(o.getByRole('alert').filter({hasText:'Mark ready within'})).toHaveCount(0)" }, { "line": 44, "expression": "expect(o.getByRole('alert').filter({hasText:'Mark ready within'}))" } ] }, { "observation": "Roster locked", "statements": [ { "line": 45, "expression": "expect(b.getByText(/Roster locked/)).toBeVisible()" }, { "line": 45, "expression": "expect(b.getByText(/Roster locked/))" } ] } ], "executions": [ { "title": "two actual clients ready, lock their roster and close below two without promoting the observer", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "starts a five-second warning", "sourceTest": "starts a five-second warning only at all-but-one, then locks immediately on final ready", "line": 51, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "holdoutDueAt", "statements": [ { "line": 52, "expression": "expect(room.holdoutDueAt).toBeNull()" }, { "line": 52, "expression": "expect(room.holdoutDueAt)" }, { "line": 53, "expression": "expect(room.holdoutDueAt).toBe(5200)" }, { "line": 53, "expression": "expect(room.holdoutDueAt)" } ] }, { "observation": "countdownDueAt", "statements": [ { "line": 55, "expression": "expect(room.countdownDueAt).toBe(15199)" }, { "line": 55, "expression": "expect(room.countdownDueAt)" } ] }, { "observation": "expireLobby", "statements": [ { "line": 56, "expression": "expect(expireLobby(room,stale,5200)).toEqual(failure('CONFLICT'))" }, { "line": 56, "expression": "expect(expireLobby(room,stale,5200))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 starts a five-second warning only at all-but-one, then locks immediately on final ready", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-32-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "players": [ { "id": "p1", "ready": true }, { "id": "p2", "ready": true }, { "id": "p3", "ready": false } ], "holdout": "p3" }, "actions": [ { "type": "expire-holdout-warning" } ] }, "assertionGroups": [ { "oraclePaths": [ "players.0", "players.1", "removed", "countdown.started", "countdown.lockedRoster.0", "countdown.lockedRoster.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "removes a holdout in larger", "sourceTest": "removes a holdout in larger rooms and locks remaining roster without promoting", "line": 58, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "expireLobby", "statements": [ { "line": 60, "expression": "expect(expireLobby(room,job(room,'holdout'),4999)).toEqual(failure('CONFLICT'))" }, { "line": 60, "expression": "expect(expireLobby(room,job(room,'holdout'),4999))" } ] }, { "observation": "next.players", "statements": [ { "line": 61, "expression": "expect(next.players.map(p=>p.profileId)).toEqual(['p1','p2'])" }, { "line": 61, "expression": "expect(next.players.map(p=>p.profileId))" } ] }, { "observation": "next.observers", "statements": [ { "line": 61, "expression": "expect(next.observers).toHaveLength(1)" }, { "line": 61, "expression": "expect(next.observers)" } ] }, { "observation": "next.status", "statements": [ { "line": 61, "expression": "expect(next.status).toBe('countdown')" }, { "line": 61, "expression": "expect(next.status)" } ] }, { "observation": "next.countdownDueAt", "statements": [ { "line": 61, "expression": "expect(next.countdownDueAt).toBe(15000)" }, { "line": 61, "expression": "expect(next.countdownDueAt)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 removes a holdout in larger rooms and locks remaining roster without promoting", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-32-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "roomVersion": 9, "commands": [ "ready-p3", "expire-p3" ] }, "actions": [ { "type": "race-ready-and-warning-expiry" } ] }, "assertionGroups": [ { "oraclePaths": [ "applied.0", "stale.0", "roomVersion", "countdownStarts" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "final ready wins before warning expiry", "sourceTest": "AC-32-T03 final ready wins before warning expiry and commits exactly one countdown despite replay and stale deadline", "line": 180, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "countdownDueAt", "statements": [ { "line": 206, "expression": "expect(store.state.countdownDueAt).toBe(14999)" }, { "line": 206, "expression": "expect(store.state.countdownDueAt)" } ] }, { "observation": "store.state.version", "statements": [ { "line": 207, "expression": "expect(store.state.version).toBe(room.version + 1)" }, { "line": 207, "expression": "expect(store.state.version)" } ] }, { "observation": "service.expire", "statements": [ { "line": 212, "expression": "expect(await service.expire(warning)).toEqual(failure(\"CONFLICT\"))" }, { "line": 212, "expression": "expect(await service.expire(warning))" } ] }, { "observation": "store.commits", "statements": [ { "line": 213, "expression": "expect(store.commits).toHaveLength(1)" }, { "line": 213, "expression": "expect(store.commits)" }, { "line": 214, "expression": "expect(store.commits[0].transition.events).toHaveLength(1)" }, { "line": 214, "expression": "expect(store.commits[0].transition.events)" } ] } ], "executions": [ { "title": "AC-32-T03 final ready wins before warning expiry and commits exactly one countdown despite replay and stale deadline", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "enforces overdue holdout", "sourceTest": "enforces overdue holdout under the action lock even when its worker is leased elsewhere", "line": 111, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "h.events", "statements": [ { "line": 115, "expression": "expect(h.events).toHaveLength(1)" }, { "line": 115, "expression": "expect(h.events)" }, { "line": 116, "expression": "expect(h.events).toHaveLength(1)" }, { "line": 116, "expression": "expect(h.events)" } ] }, { "observation": "h.room().status", "statements": [ { "line": 115, "expression": "expect(h.room().status).toBe('countdown')" }, { "line": 115, "expression": "expect(h.room().status)" } ] }, { "observation": "service.act", "statements": [ { "line": 116, "expression": "expect(await h.service.act(command,{type:'ready',ready:true})).toEqual(result)" }, { "line": 116, "expression": "expect(await h.service.act(command,{type:'ready',ready:true}))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 enforces overdue holdout under the action lock even when its worker is leased elsewhere", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-32-T04", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "players": [ { "id": "p1", "ready": true }, { "id": "p2", "ready": false } ], "holdout": "p2" }, "actions": [ { "type": "expire-two-player-warning" } ] }, "assertionGroups": [ { "oraclePaths": [ "players.0", "players.1", "removed", "countdown", "roomState" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "retains exactly-two holdout", "sourceTest": "retains exactly-two holdout without repeatedly warning and waits for both ready", "line": 63, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "room.status", "statements": [ { "line": 65, "expression": "expect(room.status).toBe('waiting')" }, { "line": 65, "expression": "expect(room.status)" } ] }, { "observation": "room.players", "statements": [ { "line": 65, "expression": "expect(room.players).toHaveLength(2)" }, { "line": 65, "expression": "expect(room.players)" } ] }, { "observation": "room.holdoutDueAt", "statements": [ { "line": 65, "expression": "expect(room.holdoutDueAt).toBeNull()" }, { "line": 65, "expression": "expect(room.holdoutDueAt)" } ] }, { "observation": "setReady", "statements": [ { "line": 67, "expression": "expect(value(setReady(room,'p2',true,20000)).status).toBe('countdown')" }, { "line": 67, "expression": "expect(value(setReady(room,'p2',true,20000)).status)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 retains exactly-two holdout without repeatedly warning and waits for both ready", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-33-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "roomVersion": 3, "players": [ "p1", "p2" ], "idleMs": 30000 }, "actions": [ { "type": "expire-idle-readiness-deadline" } ] }, "assertionGroups": [ { "oraclePaths": [ "prompts.0.roomVersion", "prompts.0.recipients.0", "prompts.0.recipients.1", "repeatedCallbackPrompts", "changedVersionCallbackPrompts", "resetDelayMs" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "all five membership/readiness changes", "sourceTest": "AC-33 supplemental all five membership/readiness changes silence old idle deadlines, prompt the current roster once and suppress prompts after lock", "line": 473, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "expireLobby", "statements": [ { "line": 503, "expression": "expect(expireLobby(room, old, 30000).ok).toBe(false)" }, { "line": 503, "expression": "expect(expireLobby(room, old, 30000).ok)" }, { "line": 515, "expression": "expect(expireLobby(prompted, fresh, 31001).ok).toBe(false)" }, { "line": 515, "expression": "expect(expireLobby(prompted, fresh, 31001).ok)" }, { "line": 520, "expression": "expect(expireLobby(locked, fresh, 31000).ok).toBe(false)" }, { "line": 520, "expression": "expect(expireLobby(locked, fresh, 31000).ok)" } ] }, { "observation": "fresh.dueAt", "statements": [ { "line": 509, "expression": "expect(fresh.dueAt).toBe(31000)" }, { "line": 509, "expression": "expect(fresh.dueAt)" } ] }, { "observation": "prompted.players", "statements": [ { "line": 512, "expression": "expect(prompted.players.map((p) => p.profileId)).toEqual(\n room.players.map((p) => p.profileId),\n )" }, { "line": 512, "expression": "expect(prompted.players.map((p) => p.profileId))" } ] }, { "observation": "locked.status", "statements": [ { "line": 519, "expression": "expect(locked.status).toBe(\"countdown\")" }, { "line": 519, "expression": "expect(locked.status)" } ] } ], "executions": [ { "title": "AC-33 supplemental all five membership/readiness changes silence old idle deadlines, prompt the current roster once and suppress prompts after lock", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "prompts once after 30s", "sourceTest": "prompts once after 30s, preserves the interval across presence, and resets on membership/readiness", "line": 79, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "dueAt", "statements": [ { "line": 81, "expression": "expect(job(room,'idle').dueAt).toBe(30000)" }, { "line": 81, "expression": "expect(job(room,'idle').dueAt)" }, { "line": 85, "expression": "expect(job(room,'idle').dueAt).toBe(62000)" }, { "line": 85, "expression": "expect(job(room,'idle').dueAt)" } ] }, { "observation": "expireLobby", "statements": [ { "line": 81, "expression": "expect(expireLobby(room,old,30000)).toEqual(failure('CONFLICT'))" }, { "line": 81, "expression": "expect(expireLobby(room,old,30000))" }, { "line": 82, "expression": "expect(expireLobby(room,job(room,'idle'),29999)).toEqual(failure('CONFLICT'))" }, { "line": 82, "expression": "expect(expireLobby(room,job(room,'idle'),29999))" } ] }, { "observation": "idlePromptedVersion", "statements": [ { "line": 83, "expression": "expect(room.idlePromptedVersion).not.toBeNull()" }, { "line": 83, "expression": "expect(room.idlePromptedVersion)" }, { "line": 84, "expression": "expect(room.idlePromptedVersion).not.toBeNull()" }, { "line": 84, "expression": "expect(room.idlePromptedVersion)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 prompts once after 30s, preserves the interval across presence, and resets on membership/readiness", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-37-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "commandId": "cmd-1", "committedState": { "version": 4 }, "quota": "exhausted" }, "actions": [ { "type": "attempt-quota-limited-operation" } ] }, "assertionGroups": [ { "oraclePaths": [ "result.code", "result.retryableWithCommandId", "stateAfter.version", "providerCalls.0", "paidFallbackCalls" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual quota rejection produces SERVICE_LIMIT before decision with committed state untouched and identical-command retry succeeds on recovery. Stalled request releases busy state. Browser storage SERVICE_LIMIT is followed by explicit identical create retry to one confirmed room; no paid fallback exists.", "bindings": [ { "file": "tests/application/command.test.ts", "title": "runs the unchanged AC-37-T01", "sourceTest": "runs the unchanged AC-37-T01 oracle against actual command/quota observations", "line": 208, "sourceHash": "1292fc7e9ab4c06db1e19b1fb640c5e81a6812e6e350ea32bc419184ec157d85", "assertedObservations": [ { "observation": "assertObservation", "statements": [ { "line": 232, "expression": "scenario?.assertObservation({\n result: outcome.ok\n ? { code: \"unexpected-success\" }\n : { ...outcome.error },\n stateAfter: { version: h.state.version },\n providerCalls,\n paidFallbackCalls: providerCalls.filter(\n (provider) => provider !== \"free-tier\",\n ).length,\n })" } ] } ], "executions": [ { "title": "Phase 1B command transaction boundary runs the unchanged AC-37-T01 oracle against actual command/quota observations", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/command.ts", "src/web/play-hub.tsx", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-38-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "activeAcIds": [ "AC-01", "AC-02" ], "mappings": [ { "ac": "AC-01", "tests": [ "AC-01-T01" ], "status": "mock-validated" }, { "ac": "AC-02", "tests": [ "AC-02-T01" ], "status": "mock-validated" } ] }, "actions": [ { "type": "validate-traceability-manifest" } ] }, "assertionGroups": [ { "oraclePaths": [ "missingAcIds", "missingTestIds", "incompleteMockRecords", "deploymentEvidenceRequired" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "45 active IDs/67 primary IDs/27 supplemental clauses are mapped to existing executable evidence. Missing AC/test/record/assertions, planned-only records, duplicate IDs and empty active set are rejected; all named semantic controls remain unchanged. The final manifest binds each oracle leaf to counterpart evidence and fixture discrepancies.", "bindings": [ { "file": "tests/traceability/ac-inventory.test.ts", "title": "validates concrete manifest", "sourceTest": "validates concrete manifest mappings and mock-validation records", "line": 38, "sourceHash": "f702fc61234b59a6ab01b226772d3d9a7179ab355db933076b0c67ccb87a8701", "assertedObservations": [ { "observation": "validateTraceabilityManifest", "statements": [ { "line": 48, "expression": "expect(validateTraceabilityManifest(complete)).toEqual({ valid: true })" }, { "line": 48, "expression": "expect(validateTraceabilityManifest(complete))" }, { "line": 51, "expression": "expect(validateTraceabilityManifest(planned)).toEqual({ valid: false, code: 'incomplete-record' })" }, { "line": 51, "expression": "expect(validateTraceabilityManifest(planned))" }, { "line": 52, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: complete.mappings.slice(0, 1) })).toEqual({ valid: false, code: 'missing-ac' })" }, { "line": 52, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: complete.mappings.slice(0, 1) }))" }, { "line": 53, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: [{ acId: 'AC-01', testIds: ['AC-01-T99'] }, complete.mappings[1]] })).toEqual({ valid: false, code: 'missing-test' })" }, { "line": 53, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: [{ acId: 'AC-01', testIds: ['AC-01-T99'] }, complete.mappings[1]] }))" }, { "line": 54, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: complete.mockValidationRecords.slice(0, 1) })).toEqual({ valid: false, code: 'missing-record' })" }, { "line": 54, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: complete.mockValidationRecords.slice(0, 1) }))" }, { "line": 55, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: [{ ...complete.mockValidationRecords[0], assertions: [] }, complete.mockValidationRecords[1]] })).toEqual({ valid: false, code: 'incomplete-record' })" }, { "line": 55, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: [{ ...complete.mockValidationRecords[0], assertions: [] }, complete.mockValidationRecords[1]] }))" }, { "line": 56, "expression": "expect(validateTraceabilityManifest({ activeAcIds: [], mappings: [], existingTestIds: [], mockValidationRecords: [] })).toEqual({ valid: false, code: 'empty-active-set' })" }, { "line": 56, "expression": "expect(validateTraceabilityManifest({ activeAcIds: [], mappings: [], existingTestIds: [], mockValidationRecords: [] }))" } ] } ], "executions": [ { "title": "AC test inventory traceability (AC-38) validates concrete manifest mappings and mock-validation records", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "tests/traceability/inventory-validator.ts" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-39-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "manifest": { "dependencies": { "vitest": "5.0.5" } }, "lockfile": { "packages": { "node_modules/vitest": { "version": "5.0.5", "license": "MIT" } } }, "attributions": [ { "name": "vitest", "purpose": "test runner", "license": "MIT", "upstream": "https://github.com/vitest-dev/vitest", "version": "5.0.5" } ] }, "actions": [ { "type": "validate-attributions" } ] }, "assertionGroups": [ { "oraclePaths": [ "directNames.0", "attributedNames.0", "records.0.name", "records.0.purpose", "records.0.license", "records.0.upstream", "records.0.declaredVersion", "records.0.lockedVersion" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Installed direct package versions/licenses match actual lock and purpose/upstream attribution rows. Original SVG Recursive font copyright/OFL text and Unicode source/license/hash are preserved; six background SVGs are original project artwork.", "bindings": [ { "file": "tests/traceability/ac-inventory.test.ts", "title": "records each installed direct", "sourceTest": "records each installed direct dependency purpose, upstream, exact locked version, and license", "line": 71, "sourceHash": "f702fc61234b59a6ab01b226772d3d9a7179ab355db933076b0c67ccb87a8701", "assertedObservations": [ { "observation": "packageJson.license", "statements": [ { "line": 72, "expression": "expect(packageJson.license).toBe('GPL-2.0-only')" }, { "line": 72, "expression": "expect(packageJson.license)" } ] }, { "observation": "columns", "statements": [ { "line": 78, "expression": "expect(columns[2], `missing purpose for ${name}`).not.toBe('')" }, { "line": 78, "expression": "expect(columns[2], `missing purpose for ${name}`)" }, { "line": 79, "expression": "expect(columns[3], `version mismatch for ${name}`).toBe(version)" }, { "line": 79, "expression": "expect(columns[3], `version mismatch for ${name}`)" }, { "line": 80, "expression": "expect(columns[4], `missing registry source for ${name}`).toMatch(/^\\[.+\\]\\(https:\\/\\/.+\\)$/)" }, { "line": 80, "expression": "expect(columns[4], `missing registry source for ${name}`)" }, { "line": 81, "expression": "expect(columns[6], `missing upstream source for ${name}`).toMatch(/^\\[.+\\]\\(https:\\/\\/.+\\)$/)" }, { "line": 81, "expression": "expect(columns[6], `missing upstream source for ${name}`)" }, { "line": 85, "expression": "expect(columns[5], `license mismatch for ${name}`).toBe(installed.license)" }, { "line": 85, "expression": "expect(columns[5], `license mismatch for ${name}`)" } ] }, { "observation": "locked", "statements": [ { "line": 87, "expression": "expect(locked?.version, `lockfile version mismatch for ${name}`).toBe(version)" }, { "line": 87, "expression": "expect(locked?.version, `lockfile version mismatch for ${name}`)" }, { "line": 88, "expression": "expect(locked?.license, `lockfile license mismatch for ${name}`).toBe(installed.license)" }, { "line": 88, "expression": "expect(locked?.license, `lockfile license mismatch for ${name}`)" } ] }, { "observation": "installed", "statements": [ { "line": 84, "expression": "expect(installed.version, `installed version mismatch for ${name}`).toBe(version)" }, { "line": 84, "expression": "expect(installed.version, `installed version mismatch for ${name}`)" }, { "line": 85, "expression": "expect(columns[5], `license mismatch for ${name}`).toBe(installed.license)" }, { "line": 88, "expression": "expect(locked?.license, `lockfile license mismatch for ${name}`).toBe(installed.license)" } ] } ], "executions": [ { "title": "direct dependency attribution (AC-39) records each installed direct dependency purpose, upstream, exact locked version, and license", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/catalog-seed.test.ts", "title": "applies the official full default Unicode", "sourceTest": "applies the official full default Unicode C+F mappings, including expansions, without Turkish locale folding", "line": 11, "sourceHash": "74fe5ad464f763fbea257818146eb9de80d709f05ddef26ba91206ee6a399532", "assertedObservations": [ { "observation": "UNICODE LICENSE V3", "statements": [ { "line": 14, "expression": "expect(readFileSync('third_party/unicode/LICENSE.txt','utf8')).toContain('UNICODE LICENSE V3')" } ] }, { "observation": "createHash", "statements": [ { "line": 15, "expression": "expect(createHash('sha256').update(source).digest('hex')).toBe('a004797658a457bec4dc11683e39f69249ea3b595b752dbea6721c4c9f587b0d')" }, { "line": 15, "expression": "expect(createHash('sha256').update(source).digest('hex'))" } ] } ], "executions": [ { "title": "applies the official full default Unicode C+F mappings, including expansions, without Turkish locale folding", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "package.json", "package-lock.json", "docs/ATTRIBUTIONS.md", "LICENSE", "third_party/unicode/CaseFolding-18.0.0.txt" ], "fixtureDiscrepancy": [ "Dependency versions and asset inventory are the actual locked project, not the historical example packages." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-41-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "requiredCommit": "594f6753ebd747a69608bc4ded9fa19902cff5fc", "requiredEntries": [ "LICENSE", "docs/ATTRIBUTIONS.md", "src/index.ts", "sbom/594f6753ebd747a69608bc4ded9fa19902cff5fc.cdx.json" ], "candidateArchives": [ "valid.tar", "missing-source.tar", "missing-license.tar", "missing-sbom.tar", "wrong.tar", "invalid.tar", "empty.tar" ] }, "actions": [ { "type": "extract-temporary-archives" }, { "type": "validate-source-manifest" }, { "type": "validate-cyclonedx" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.name", "results.0.entries.0", "results.0.entries.1", "results.0.entries.2", "results.0.entries.3", "results.0.bom.bomFormat", "results.0.bom.specVersion", "results.0.bom.serialNumber", "results.0.bom.version", "results.0.bom.metadata.component.type", "results.0.bom.metadata.component.name", "results.0.bom.metadata.component.version", "results.0.bom.components", "results.0.decision", "results.1.name", "results.1.entries.0", "results.1.entries.1", "results.1.entries.2", "results.1.decision", "results.2.name", "results.2.entries.0", "results.2.entries.1", "results.2.entries.2", "results.2.decision", "results.3.name", "results.3.entries.0", "results.3.entries.1", "results.3.entries.2", "results.3.decision", "results.4.name", "results.4.entries.0", "results.4.entries.1", "results.4.entries.2", "results.4.entries.3", "results.4.decision", "results.5.name", "results.5.entries.0", "results.5.entries.1", "results.5.entries.2", "results.5.entries.3", "results.5.decision", "results.6.name", "results.6.entries", "results.6.decision" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Temporary exact HEAD Git snapshot tar.gz is read back against every independently fetched Git blob hash, matching fixture CycloneDX 1.6 BOM and SHA256 sidecar. Missing/malformed/wrong-commit report, source alteration/removal and sidecar corruption fail. Original ZIP parser/truncation/schema/path controls remain.", "bindings": [ { "file": "tests/acceptance/source-archive.test.ts", "title": "exact Git snapshot tar.gz", "sourceTest": "AC-41/42 exact Git snapshot tar.gz, matching fixture BOM, complete notices and SHA256 sidecar reject tampering", "line": 17, "sourceHash": "9d50c2f30ad2b5a53549849bfce68f972fa6176e415b0e4e7de09dc7550cb190", "assertedObservations": [ { "observation": "inspect", "statements": [ { "line": 94, "expression": "expect(await inspect()).toBe(\"accepted\")" }, { "line": 94, "expression": "expect(await inspect())" }, { "line": 113, "expression": "expect(await inspect()).toBe(\"checksum\")" }, { "line": 113, "expression": "expect(await inspect())" }, { "line": 124, "expression": "expect(await inspect()).toBe(\"commit-mismatch\")" }, { "line": 124, "expression": "expect(await inspect())" }, { "line": 127, "expression": "expect(await inspect()).toBe(\"invalid-sbom\")" }, { "line": 127, "expression": "expect(await inspect())" }, { "line": 130, "expression": "expect(await inspect()).toBe(\"missing-sbom\")" }, { "line": 130, "expression": "expect(await inspect())" }, { "line": 134, "expression": "expect(await inspect()).toBe(\"altered-source\")" }, { "line": 134, "expression": "expect(await inspect())" }, { "line": 137, "expression": "expect(await inspect()).toBe(\"missing-source\")" }, { "line": 137, "expression": "expect(await inspect())" } ] }, { "observation": "manifest.has", "statements": [ { "line": 95, "expression": "expect(manifest.has(\"LICENSE\")).toBe(true)" }, { "line": 95, "expression": "expect(manifest.has(\"LICENSE\"))" }, { "line": 96, "expression": "expect(manifest.has(\"docs/ATTRIBUTIONS.md\")).toBe(true)" }, { "line": 96, "expression": "expect(manifest.has(\"docs/ATTRIBUTIONS.md\"))" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\")).toBe(true)" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\"))" } ] }, { "observation": "sha256", "statements": [ { "line": 98, "expression": "expect(sha256(readFileSync(join(source, \"LICENSE\")))).toBe(\n \"aaf135472f81c5b4a0dca9367e5bb5e9750032b5bebe5442b36e4c0a47430df3\",\n )" }, { "line": 98, "expression": "expect(sha256(readFileSync(join(source, \"LICENSE\"))))" } ] }, { "observation": "svg", "statements": [ { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\")).toBe(true)" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\"))" }, { "line": 111, "expression": "expect(svg).toContain(notice)" }, { "line": 111, "expression": "expect(svg)" } ] } ], "executions": [ { "title": "AC-41/42 exact Git snapshot tar.gz, matching fixture BOM, complete notices and SHA256 sidecar reject tampering", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/ac-contracts/oracle-supplemental-validation.test.ts", "title": "creates and inspects ZIP", "sourceTest": "AC-41 creates and inspects ZIP archives using the official pinned CycloneDX 1.6 validator", "line": 59, "sourceHash": "7fcb507554fc0c71abaa131d3e9c45f3051628610aa0404aa2fd090f81cfc3f2", "assertedObservations": [ { "observation": "validateSourceArchiveZip", "statements": [ { "line": 61, "expression": "expect(await validateSourceArchiveZip(await roundTripTemporaryArchive(baseArchive), EXPECTED_COMMIT, sourceManifest)).toBe('accepted')" }, { "line": 61, "expression": "expect(await validateSourceArchiveZip(await roundTripTemporaryArchive(baseArchive), EXPECTED_COMMIT, sourceManifest))" }, { "line": 62, "expression": "expect(await validateSourceArchiveZip(await roundTripTemporaryArchive(baseArchive), EXPECTED_COMMIT, {})).toBe('incomplete-source')" }, { "line": 62, "expression": "expect(await validateSourceArchiveZip(await roundTripTemporaryArchive(baseArchive), EXPECTED_COMMIT, {}))" }, { "line": 63, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({}), EXPECTED_COMMIT, sourceManifest)).toBe('empty-archive')" }, { "line": 63, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({}), EXPECTED_COMMIT, sourceManifest))" }, { "line": 65, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutLicense), EXPECTED_COMMIT, sourceManifest)).toBe('missing-license')" }, { "line": 65, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutLicense), EXPECTED_COMMIT, sourceManifest))" }, { "line": 67, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutNotices), EXPECTED_COMMIT, sourceManifest)).toBe('missing-notices')" }, { "line": 67, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutNotices), EXPECTED_COMMIT, sourceManifest))" }, { "line": 71, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutSource), EXPECTED_COMMIT, sourceManifest)).toBe('incomplete-source')" }, { "line": 71, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutSource), EXPECTED_COMMIT, sourceManifest))" }, { "line": 72, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [sourceToRemove]: Buffer.alloc(0) }), EXPECTED_COMMIT, sourceManifest)).toBe('altered-source')" }, { "line": 72, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [sourceToRemove]: Buffer.alloc(0) }), EXPECTED_COMMIT, sourceManifest))" }, { "line": 75, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutBom), EXPECTED_COMMIT, sourceManifest)).toBe('missing-sbom')" }, { "line": 75, "expression": "expect(await validateSourceArchiveZip(await createZipArchive(withoutBom), EXPECTED_COMMIT, sourceManifest))" }, { "line": 76, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...withoutBom, 'sbom/def456.cdx.json': validBom }), EXPECTED_COMMIT, sourceManifest)).toBe('commit-mismatch')" }, { "line": 76, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...withoutBom, 'sbom/def456.cdx.json': validBom }), EXPECTED_COMMIT, sourceManifest))" }, { "line": 77, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...withoutBom, [`swbom/${EXPECTED_COMMIT}.cdx.json`]: validBom }), EXPECTED_COMMIT, sourceManifest)).toBe('missing-sbom')" }, { "line": 77, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...withoutBom, [`swbom/${EXPECTED_COMMIT}.cdx.json`]: validBom }), EXPECTED_COMMIT, sourceManifest))" }, { "line": 78, "expression": "expect(await validateSourceArchiveZip((await createZipArchive(baseArchive)).subarray(0, 32), EXPECTED_COMMIT, sourceManifest)).toBe('truncated-archive')" }, { "line": 78, "expression": "expect(await validateSourceArchiveZip((await createZipArchive(baseArchive)).subarray(0, 32), EXPECTED_COMMIT, sourceManifest))" }, { "line": 85, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [`sbom/${EXPECTED_COMMIT}.cdx.json`]: JSON.stringify(bom) }), EXPECTED_COMMIT, sourceManifest)).toBe('invalid-sbom')" }, { "line": 85, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [`sbom/${EXPECTED_COMMIT}.cdx.json`]: JSON.stringify(bom) }), EXPECTED_COMMIT, sourceManifest))" }, { "line": 88, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [`sbom/${EXPECTED_COMMIT}.cdx.json`]: JSON.stringify(wrongMetadata) }), EXPECTED_COMMIT, sourceManifest)).toBe('commit-mismatch')" }, { "line": 88, "expression": "expect(await validateSourceArchiveZip(await createZipArchive({ ...baseArchive, [`sbom/${EXPECTED_COMMIT}.cdx.json`]: JSON.stringify(wrongMetadata) }), EXPECTED_COMMIT, sourceManifest))" } ] } ], "executions": [ { "title": "AC-41 creates and inspects ZIP archives using the official pinned CycloneDX 1.6 validator", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "tests/ac-contracts/archive-fixture-validator.ts", "docs/SWBOM.md" ], "fixtureDiscrepancy": [ "The original ZIP fixture keeps its historical commit token; added tar.gz counterpart uses current immutable Git HEAD and actual snapshot bytes. Fixture BOM has no deployed inventory claim; operational packaging remains Phase 7." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-42-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "requiredLicense": "GPL-2.0-only", "requiredLicenseSha256": "aaf135472f81c5b4a0dca9367e5bb5e9750032b5bebe5442b36e4c0a47430df3", "requiredNoticesSha256": "7e112447507249b6c71ee1c74bf357c0382c042b29325ebce00e268f67cb7c43", "requiredAsset": "docs/word_association_round_flow.svg", "requiredAssetSha256": "da03873d53e31a9e7d84d772cab14bbfcd47bfdf0c285580a468dc39822c2bb0", "bundledComponents": [ "Recursive font" ] }, "actions": [ { "type": "extract-temporary-archive" }, { "type": "hash-license-notices-and-assets" }, { "type": "inspect-embedded-font-notice" } ] }, "assertionGroups": [ { "oraclePaths": [ "entries.0.path", "entries.0.sha256", "entries.0.bytes", "entries.1.path", "entries.1.sha256", "entries.1.bytes", "entries.1.includes.0", "entries.1.includes.1", "entries.2.path", "entries.2.sha256", "entries.2.bytes", "entries.2.includes.0", "entries.2.includes.1", "entries.2.includes.2", "entries.2.includes.3", "spdx" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Exact source archive preserves complete pinned GPL-2 text, attribution and complete Recursive copyright/OFL notice. Missing/truncated license, altered source, omitted notices and missing font terms are rejected.", "bindings": [ { "file": "tests/acceptance/source-archive.test.ts", "title": "exact Git snapshot tar.gz", "sourceTest": "AC-41/42 exact Git snapshot tar.gz, matching fixture BOM, complete notices and SHA256 sidecar reject tampering", "line": 17, "sourceHash": "9d50c2f30ad2b5a53549849bfce68f972fa6176e415b0e4e7de09dc7550cb190", "assertedObservations": [ { "observation": "inspect", "statements": [ { "line": 94, "expression": "expect(await inspect()).toBe(\"accepted\")" }, { "line": 94, "expression": "expect(await inspect())" }, { "line": 113, "expression": "expect(await inspect()).toBe(\"checksum\")" }, { "line": 113, "expression": "expect(await inspect())" }, { "line": 124, "expression": "expect(await inspect()).toBe(\"commit-mismatch\")" }, { "line": 124, "expression": "expect(await inspect())" }, { "line": 127, "expression": "expect(await inspect()).toBe(\"invalid-sbom\")" }, { "line": 127, "expression": "expect(await inspect())" }, { "line": 130, "expression": "expect(await inspect()).toBe(\"missing-sbom\")" }, { "line": 130, "expression": "expect(await inspect())" }, { "line": 134, "expression": "expect(await inspect()).toBe(\"altered-source\")" }, { "line": 134, "expression": "expect(await inspect())" }, { "line": 137, "expression": "expect(await inspect()).toBe(\"missing-source\")" }, { "line": 137, "expression": "expect(await inspect())" } ] }, { "observation": "manifest.has", "statements": [ { "line": 95, "expression": "expect(manifest.has(\"LICENSE\")).toBe(true)" }, { "line": 95, "expression": "expect(manifest.has(\"LICENSE\"))" }, { "line": 96, "expression": "expect(manifest.has(\"docs/ATTRIBUTIONS.md\")).toBe(true)" }, { "line": 96, "expression": "expect(manifest.has(\"docs/ATTRIBUTIONS.md\"))" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\")).toBe(true)" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\"))" } ] }, { "observation": "sha256", "statements": [ { "line": 98, "expression": "expect(sha256(readFileSync(join(source, \"LICENSE\")))).toBe(\n \"aaf135472f81c5b4a0dca9367e5bb5e9750032b5bebe5442b36e4c0a47430df3\",\n )" }, { "line": 98, "expression": "expect(sha256(readFileSync(join(source, \"LICENSE\"))))" } ] }, { "observation": "svg", "statements": [ { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\")).toBe(true)" }, { "line": 97, "expression": "expect(manifest.has(\"docs/word_association_round_flow.svg\"))" }, { "line": 111, "expression": "expect(svg).toContain(notice)" }, { "line": 111, "expression": "expect(svg)" } ] } ], "executions": [ { "title": "AC-41/42 exact Git snapshot tar.gz, matching fixture BOM, complete notices and SHA256 sidecar reject tampering", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/ac-contracts/oracle-supplemental-validation.test.ts", "title": "validates complete GPL", "sourceTest": "AC-42 validates complete GPL and the actual Recursive-bearing SVG notice", "line": 91, "sourceHash": "7fcb507554fc0c71abaa131d3e9c45f3051628610aa0404aa2fd090f81cfc3f2", "assertedObservations": [ { "observation": "validateReleaseNoticesZip", "statements": [ { "line": 100, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(baseArchive), expected)).toBe(true)" }, { "line": 100, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(baseArchive), expected))" }, { "line": 101, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, LICENSE: 'GNU GENERAL PUBLIC LICENSE\\nVersion 2, June 1991' }), expected)).toBe(false)" }, { "line": 101, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, LICENSE: 'GNU GENERAL PUBLIC LICENSE\\nVersion 2, June 1991' }), expected))" }, { "line": 102, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/ATTRIBUTIONS.md': 'Recursive font — OFL-1.1' }), expected)).toBe(false)" }, { "line": 102, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/ATTRIBUTIONS.md': 'Recursive font — OFL-1.1' }), expected))" }, { "line": 104, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(withoutAsset), expected)).toBe(false)" }, { "line": 104, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(withoutAsset), expected))" }, { "line": 107, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.replace(removed, '') }), expected)).toBe(false)" }, { "line": 107, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.replace(removed, '') }), expected))" }, { "line": 109, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.slice(0, assetText.indexOf('TERMINATION')) }), expected)).toBe(false)" }, { "line": 109, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.slice(0, assetText.indexOf('TERMINATION')) }), expected))" } ] } ], "executions": [ { "title": "AC-42 validates complete GPL and the actual Recursive-bearing SVG notice", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "LICENSE", "docs/ATTRIBUTIONS.md", "docs/word_association_round_flow.svg" ], "fixtureDiscrepancy": [ "Exact-candidate tar.gz is additional temporary contract evidence; original ZIP tests and pinned notice hashes are preserved." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-43-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "signedIn": false, "rules": { "version": "2.6", "updated": "2026-10-26" } }, "actions": [ { "type": "render-anonymous-landing" } ] }, "assertionGroups": [ { "oraclePaths": [ "sections.0", "sections.1", "sections.2", "rules.version", "rules.updated", "emptyState", "unavailableState", "signInRequired" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/web/landing.test.ts", "title": "renders rules, ranking and rooms", "sourceTest": "renders rules, ranking and rooms without sign-in and distinguishes a successful empty query", "line": 88, "sourceHash": "ffcc2c27b12a0bb2dcb08def7cbc6f7e33156214ce5ad8956fd9c95a284725df", "assertedObservations": [ { "observation": "screen", "statements": [ { "line": 94, "expression": "expect(screen.getByRole(\"heading\", { name: \"Leaderboard\" })).toBeDefined()" }, { "line": 94, "expression": "expect(screen.getByRole(\"heading\", { name: \"Leaderboard\" }))" }, { "line": 95, "expression": "expect(screen.getByRole(\"heading\", { name: \"Open lobbies\" })).toBeDefined()" }, { "line": 95, "expression": "expect(screen.getByRole(\"heading\", { name: \"Open lobbies\" }))" }, { "line": 96, "expression": "expect(screen.getByRole(\"heading\", { name: \"How to play\" })).toBeDefined()" }, { "line": 96, "expression": "expect(screen.getByRole(\"heading\", { name: \"How to play\" }))" }, { "line": 97, "expression": "expect(screen.getByText(\"No games yet\")).toBeDefined()" }, { "line": 97, "expression": "expect(screen.getByText(\"No games yet\"))" }, { "line": 98, "expression": "expect(screen.getByText(\"No joinable rooms\")).toBeDefined()" }, { "line": 98, "expression": "expect(screen.getByText(\"No joinable rooms\"))" } ] } ], "executions": [ { "title": "AC-43 anonymous landing states renders rules, ranking and rooms without sign-in and distinguishes a successful empty query", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/web/landing.test.ts", "title": "shows recoverable unavailable", "sourceTest": "shows recoverable unavailable states rather than reporting empty rankings on failure", "line": 101, "sourceHash": "ffcc2c27b12a0bb2dcb08def7cbc6f7e33156214ce5ad8956fd9c95a284725df", "assertedObservations": [ { "observation": "screen", "statements": [ { "line": 108, "expression": "expect(screen.getAllByText(\"Try again later\")).toHaveLength(2)" }, { "line": 108, "expression": "expect(screen.getAllByText(\"Try again later\"))" }, { "line": 110, "expression": "expect(screen.queryByText(\"No games yet\")).toBeNull()" }, { "line": 110, "expression": "expect(screen.queryByText(\"No games yet\"))" }, { "line": 111, "expression": "expect(\n screen.getByRole(\"button\", { name: \"Refresh public information\" }),\n ).toBeDefined()" }, { "line": 111, "expression": "expect(\n screen.getByRole(\"button\", { name: \"Refresh public information\" }),\n )" } ] } ], "executions": [ { "title": "AC-43 anonymous landing states shows recoverable unavailable states rather than reporting empty rankings on failure", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-43-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "nowMs": 0, "profiles": [ { "id": "p2", "wins": 3, "points": 20, "earliestWin": 200, "public": true, "kind": "member" }, { "id": "p1", "wins": 3, "points": 20, "earliestWin": 100, "public": true, "kind": "member" }, { "id": "g1", "wins": 4, "points": 5, "earliestWin": 50, "public": true, "kind": "guest", "optedIn": true }, { "id": "g2", "wins": 9, "points": 99, "earliestWin": 1, "public": true, "kind": "guest", "optedIn": false } ] }, "actions": [ { "type": "refresh-public-leaderboard" } ] }, "assertionGroups": [ { "oraclePaths": [ "rows.0.alias", "rows.0.profileId", "rows.1.alias", "rows.1.profileId", "rows.2.alias", "rows.2.profileId", "excludedProfileIds.0", "refreshAtMs" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/application/public-projections.test.ts", "title": "orders lifetime wins", "sourceTest": "orders lifetime wins, points, earliest win and stable ID while excluding opt-outs", "line": 51, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "calls", "statements": [ { "line": 81, "expression": "expect(calls).toEqual([0])" }, { "line": 81, "expression": "expect(calls)" }, { "line": 84, "expression": "expect(calls).toHaveLength(2)" }, { "line": 84, "expression": "expect(calls)" }, { "line": 85, "expression": "acOracleScenarios\n .find((scenario) => scenario.id === \"AC-43-T02\")\n ?.assertObservation({\n rows: ranked.map((entry) => ({\n alias: entry.alias,\n profileId: entry.id,\n })),\n excludedProfileIds: inputs\n .filter((input) => !ranked.some((row) => row.id === input.id))\n .map((input) => input.id),\n refreshAtMs: calls[1],\n })" } ] }, { "observation": "rankPublicProfiles", "statements": [ { "line": 97, "expression": "expect(\n rankPublicProfiles([profile(\"b\"), profile(\"a\")]).map((entry) => entry.id),\n ).toEqual([\"a\", \"b\"])" }, { "line": 97, "expression": "expect(\n rankPublicProfiles([profile(\"b\"), profile(\"a\")]).map((entry) => entry.id),\n )" }, { "line": 100, "expression": "expect(\n rankPublicProfiles([\n profile(\"points\", { points: 21 }),\n profile(\"wins\", { wins: 4 }),\n ]).map((entry) => entry.id),\n ).toEqual([\"wins\", \"points\"])" }, { "line": 100, "expression": "expect(\n rankPublicProfiles([\n profile(\"points\", { points: 21 }),\n profile(\"wins\", { wins: 4 }),\n ]).map((entry) => entry.id),\n )" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections orders lifetime wins, points, earliest win and stable ID while excluding opt-outs", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/public-service.test.ts", "title": "refreshes lobbies", "sourceTest": "refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "line": 12, "sourceHash": "6e93213aad39b39d687b7bf4627c0fe42268c56d953e9e69c2e29a549a2e3fc8", "assertedObservations": [ { "observation": "repository.lobbies", "statements": [ { "line": 19, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(1)" }, { "line": 19, "expression": "expect(repository.lobbies)" }, { "line": 22, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(2)" }, { "line": 22, "expression": "expect(repository.lobbies)" } ] }, { "observation": "repository.leaderboard", "statements": [ { "line": 23, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(1)" }, { "line": 23, "expression": "expect(repository.leaderboard)" }, { "line": 26, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(2)" }, { "line": 26, "expression": "expect(repository.leaderboard)" }, { "line": 27, "expression": "expect(repository.leaderboard).toHaveBeenLastCalledWith({ offset: 0, limit: 25, revision: null })" }, { "line": 27, "expression": "expect(repository.leaderboard)" } ] } ], "executions": [ { "title": "TR-30 bounded public reads and cache ownership refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-43-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "nowMs": 60000, "rooms": [ { "id": "r1", "playerCount": 2, "capacity": 4, "state": "waiting", "createdAtMs": 0, "code": "SECRET", "playerNames": [ "A", "B" ] } ] }, "actions": [ { "type": "refresh-public-lobbies" } ] }, "assertionGroups": [ { "oraclePaths": [ "cards.0.roomId", "cards.0.playerCount", "cards.0.capacity", "cards.0.state", "cards.0.ageSeconds", "refreshAtMs" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/application/public-projections.test.ts", "title": "shows only discoverable waiting", "sourceTest": "AC-22/43 shows only discoverable waiting lobbies with free seats, and stable oldest-first order", "line": 129, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "publicLobbies", "statements": [ { "line": 137, "expression": "expect(publicLobbies(rooms, 60000).map((entry) => entry.roomId)).toEqual([\n \"a\",\n \"b\",\n ])" }, { "line": 137, "expression": "expect(publicLobbies(rooms, 60000).map((entry) => entry.roomId))" }, { "line": 141, "expression": "expect(\n publicLobbies([room(\"future\", { createdAt: 90000 })], 60000)[0]\n .ageSeconds,\n ).toBe(0)" }, { "line": 141, "expression": "expect(\n publicLobbies([room(\"future\", { createdAt: 90000 })], 60000)[0]\n .ageSeconds,\n )" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections AC-22/43 shows only discoverable waiting lobbies with free seats, and stable oldest-first order", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/public-projections.test.ts", "title": "runs unchanged AC-50", "sourceTest": "runs unchanged AC-50-T01 against the entire actual projection including poisoned private records", "line": 108, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "assertObservation", "statements": [ { "line": 114, "expression": "acOracleScenarios\n .find((scenario) => scenario.id === \"AC-50-T01\")\n ?.assertObservation(view)" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections runs unchanged AC-50-T01 against the entire actual projection including poisoned private records", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/public-service.test.ts", "title": "refreshes lobbies", "sourceTest": "refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "line": 12, "sourceHash": "6e93213aad39b39d687b7bf4627c0fe42268c56d953e9e69c2e29a549a2e3fc8", "assertedObservations": [ { "observation": "repository.lobbies", "statements": [ { "line": 19, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(1)" }, { "line": 19, "expression": "expect(repository.lobbies)" }, { "line": 22, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(2)" }, { "line": 22, "expression": "expect(repository.lobbies)" } ] }, { "observation": "repository.leaderboard", "statements": [ { "line": 23, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(1)" }, { "line": 23, "expression": "expect(repository.leaderboard)" }, { "line": 26, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(2)" }, { "line": 26, "expression": "expect(repository.leaderboard)" }, { "line": 27, "expression": "expect(repository.leaderboard).toHaveBeenLastCalledWith({ offset: 0, limit: 25, revision: null })" }, { "line": 27, "expression": "expect(repository.leaderboard)" } ] } ], "executions": [ { "title": "TR-30 bounded public reads and cache ownership refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-44-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "viewports": [ "portrait", "landscape" ], "inputs": [ "touch", "keyboard" ] }, "actions": [ { "type": "exercise-accessible-controls" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.viewport", "results.0.inputs.0", "results.0.inputs.1", "results.0.visibleFocus", "results.0.contrastRatio", "results.0.statusText", "results.1.viewport", "results.1.inputs.0", "results.1.inputs.1", "results.1.visibleFocus", "results.1.contrastRatio", "results.1.statusText" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Computed gameplay foreground/background RGBA ratios are retained unrounded; compositing over black and white gives conservative artwork bounds ≥4.5. Four viewports verify solid 3px visible keyboard focus, ≥44px primary button, Tab/Enter and touch operation. Icon selection is conveyed by accessible aria-pressed plus named icon; solved check/text and +3 points text convey status without color.", "bindings": [ { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "keyboard and touch", "sourceTest": "`AC-15/44 counterparts ${viewport.width}x${viewport.height} keyboard and touch`", "line": 149, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "scrollWidth", "statements": [ { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n ).toBe(true)" }, { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n )" } ] }, { "observation": "sample.ratios", "statements": [ { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n ).toBeGreaterThanOrEqual(4.5)" }, { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n )" } ] }, { "observation": "measured.focus", "statements": [ { "line": 242, "expression": "expect(measured.focus.style).toBe(\"solid\")" }, { "line": 242, "expression": "expect(measured.focus.style)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width)).toBeGreaterThanOrEqual(3)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width))" } ] }, { "observation": "measured.buttonHeight", "statements": [ { "line": 244, "expression": "expect(measured.buttonHeight).toBeGreaterThanOrEqual(44)" }, { "line": 244, "expression": "expect(measured.buttonHeight)" } ] }, { "observation": "Solved", "statements": [ { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n ).toBeVisible()" }, { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n )" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/)).toBeVisible()" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/))" } ] } ], "executions": [ { "title": "AC-15/44 counterparts 320x568 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 568x320 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 390x844 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 844x390 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "chosen icon persists", "sourceTest": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "line": 281, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "aria-pressed", "statements": [ { "line": 296, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 298, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" } ] }, { "observation": "frog", "statements": [ { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] }, { "observation": "final", "statements": [ { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/app/globals.css", "src/web/identity-panel.tsx", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Original contrastRatio exactly 4.5 is a threshold fixture, not an expected measurement. Actual minimum is retained, never capped. Historical literal “Owl selected” maps to accessible named icon and aria-pressed=true; score uses text +3 points without requiring a decorative arrow." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-45-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "playerId": "p1", "stage": "no-hint", "commandIds": [ "cmd-1", "cmd-1", "cmd-2" ] }, "actions": [ { "type": "retry-accepted-answer" } ] }, "assertionGroups": [ { "oraclePaths": [ "acceptances.0.commandId", "acceptances.0.scoreDelta", "scoreAfter" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-45-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-45-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-45-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "playerId": "p1", "answer": "dog", "target": "cat", "scoreBefore": 5 }, "actions": [ { "type": "submit-incorrect-answer" } ] }, "assertionGroups": [ { "oraclePaths": [ "result", "scoreAfter", "emittedEvents" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-45-T02 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-45-T02 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output.", "emittedEvents=[] means incorrect-guess broadcasts. A real MatchChanged invalidation is emitted; it contains no guess and is checked/filtered explicitly." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-45-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "target": "cat" }, "actions": [ { "type": "project-answer-visibility" } ] }, "assertionGroups": [ { "oraclePaths": [ "beforeReveal.player", "beforeReveal.observer", "afterReveal.player.target", "afterReveal.observer.target" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-45-T03 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-45-T03 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-46-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "invalidClaims": [ "signature", "issuer", "audience", "state", "nonce", "expiry" ] }, "actions": [ { "type": "validate-oidc-callbacks" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.claim", "results.0.result", "results.1.claim", "results.1.result", "results.2.claim", "results.2.result", "results.3.claim", "results.3.result", "results.4.claim", "results.4.result", "results.5.claim", "results.5.result", "profileAssociationsCreated" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual identity application handles generated guest opt-out, stable browser credential and stable verified issuer/subject member association without guest linking. Browser IndexedDB/cookie clear resets the guest; reload retains preferences; uncertain preference retry has identical payload and one mutation; stale shared-session mutation refreshes identity. Actual OIDC adapter rejects invalid signature, issuer, audience, state, nonce and expiry; invalid callbacks never associate a profile. Private API derives actor and rejects forged displayed identity/roles.", "bindings": [ { "file": "tests/integration/oidc.test.ts", "title": "rejects a malformed or untrusted token", "sourceTest": "rejects a malformed or untrusted token without returning provider details: %j", "line": 51, "sourceHash": "9e5aaa26111e31e1601254465bb4a3541b0bcb80981735bd7c34014a442964b5", "assertedObservations": [ { "observation": "verify", "statements": [ { "line": 54, "expression": "expect(await provider(options).instance.verify(callback)).toEqual(failure('NOT_AUTHENTICATED'))" }, { "line": 54, "expression": "expect(await provider(options).instance.verify(callback))" } ] } ], "executions": [ { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"wrongSignature\":true}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"iss\":\"https://wrong.example.test\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"aud\":\"wrong-audience\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"nonce\":\"wrong-nonce\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"exp\":1}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"sub\":\"\"}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"claims\":{\"nbf\":1793057668}}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" }, { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a malformed or untrusted token without returning provider details: {\"tokenStatus\":400}", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/integration/oidc.test.ts", "title": "rejects a bad state", "sourceTest": "rejects a bad state before exchanging a code and rejects issuer mismatch during discovery", "line": 56, "sourceHash": "9e5aaa26111e31e1601254465bb4a3541b0bcb80981735bd7c34014a442964b5", "assertedObservations": [ { "observation": "verify", "statements": [ { "line": 58, "expression": "expect(await instance.verify({...callback,state:'attacker-state'})).toEqual(failure('NOT_AUTHENTICATED'))" }, { "line": 58, "expression": "expect(await instance.verify({...callback,state:'attacker-state'}))" }, { "line": 60, "expression": "expect(await provider({discoveryIssuer:'https://wrong.example.test'}).instance.verify(callback)).toEqual(failure('NOT_AUTHENTICATED'))" }, { "line": 60, "expression": "expect(await provider({discoveryIssuer:'https://wrong.example.test'}).instance.verify(callback))" } ] } ], "executions": [ { "title": "AC-20/46 actual OIDC protocol validation against fake discovery, JWKS and token endpoints rejects a bad state before exchanging a code and rejects issuer mismatch during discovery", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/identity.test.ts", "title": "passes only verified identities", "sourceTest": "passes only verified identities to profile association and refuses invalid browser credentials", "line": 12, "sourceHash": "365b8b6405d3ada55c5216848a115b41a5f14be485f6fb80fa2697f969b5240c", "assertedObservations": [ { "observation": "member", "statements": [ { "line": 18, "expression": "expect(repository.member).not.toHaveBeenCalled()" }, { "line": 18, "expression": "expect(repository.member)" } ] } ], "executions": [ { "title": "AC-20/21/46 identity application boundaries passes only verified identities to profile association and refuses invalid browser credentials", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/identity.ts", "src/infrastructure/oidc.ts", "src/web/identity-panel.tsx", "src/web/guest-credential.ts" ], "fixtureDiscrepancy": [ "Provider discovery/JWKS/token transport and persistence are local fakes; no live account or provider entitlement is claimed. Browser member entry invokes application begin/completeLogin behind this verified-claims port." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-47-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "actorRole": "player", "candidateId": "n1" }, "actions": [ { "type": "attempt-moderation-review" } ] }, "assertionGroups": [ { "oraclePaths": [ "result", "moderationVersionBefore", "moderationVersionAfter", "catalogEntriesCreated" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-47-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "actorRole": "moderator", "candidates": [ "complete-approved", "incomplete", "unapproved" ] }, "actions": [ { "type": "review-catalog-candidate" } ] }, "assertionGroups": [ { "oraclePaths": [ "auditRecords.0.candidateId", "auditRecords.0.action", "auditRecords.0.actorRole", "playableIds.0" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "activation requires an approved case", "sourceTest": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "line": 28, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "activateCase", "statements": [ { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 29, "expression": "expect(state.publications).toHaveLength(1)" }, { "line": 29, "expression": "expect(state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 29, "expression": "expect(state.cases[0].playable).toBe(true)" }, { "line": 29, "expression": "expect(state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "normal independent moderator approval", "sourceTest": "AC-47-T02 normal independent moderator approval records actor reason threshold eligibility and no activation", "line": 102, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"approved\",\n at: 100,\n details: {\n caseId: id,\n previousStatus: \"pending\",\n status: \"approved\",\n nominationCount: 10,\n reason: \"Complete evidence verified\",\n futureEligible: true,\n playable: false,\n },\n })" }, { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" } ] }, { "observation": "publications", "statements": [ { "line": 176, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 176, "expression": "expect(store.state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"approved\",\n at: 100,\n details: {\n caseId: id,\n previousStatus: \"pending\",\n status: \"approved\",\n nominationCount: 10,\n reason: \"Complete evidence verified\",\n futureEligible: true,\n playable: false,\n },\n })" }, { "line": 177, "expression": "expect(store.state.cases[0].playable).toBe(false)" }, { "line": 177, "expression": "expect(store.state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-47-T02 normal independent moderator approval records actor reason threshold eligibility and no activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-47-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "originalReviewer": "m1", "seniorModerator": "m2", "proposer": "p1" }, "actions": [ { "type": "resolve-moderation-appeal" } ] }, "assertionGroups": [ { "oraclePaths": [ "appealAudit.actor", "appealAudit.action", "resolutionAudit.actor", "resolutionAudit.reason", "resolutionAudit.outcome", "originalReviewerAttempt", "secondAppealAttempt", "catalogPublications" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "allows one proposer appeal", "sourceTest": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "line": 22, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "resolveAppeal", "statements": [ { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok)" } ] }, { "observation": "appealCase", "statements": [ { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 23, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 23, "expression": "expect(state.publications)" } ] }, { "observation": "status", "statements": [ { "line": 23, "expression": "expect(state.cases[0].status).toBe(outcome==='uphold'?'rejected':'approved')" }, { "line": 23, "expression": "expect(state.cases[0].status)" } ] } ], "executions": [ { "title": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-50-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "room": { "id": "room-public-1", "playerCount": 2, "capacity": 4, "state": "waiting", "ageSeconds": 60, "code": "SECRET", "target": "cat", "guesses": [ "dog" ], "playerNames": [ "Alice" ], "moderation": { "status": "pending" }, "serviceSecret": "key" }, "leaderboard": [ { "alias": "Player Oak", "wins": 2, "points": 17 } ] }, "actions": [ { "type": "query-public-landing" } ] }, "assertionGroups": [ { "oraclePaths": [ "leaderboard.0.alias", "leaderboard.0.wins", "leaderboard.0.points", "lobbies.0.roomId", "lobbies.0.playerCount", "lobbies.0.capacity", "lobbies.0.state", "lobbies.0.ageSeconds", "rules.version", "rules.updated" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/application/public-projections.test.ts", "title": "runs unchanged AC-50", "sourceTest": "runs unchanged AC-50-T01 against the entire actual projection including poisoned private records", "line": 108, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "assertObservation", "statements": [ { "line": 114, "expression": "acOracleScenarios\n .find((scenario) => scenario.id === \"AC-50-T01\")\n ?.assertObservation(view)" } ] }, { "observation": "encoded", "statements": [ { "line": 126, "expression": "expect(encoded).not.toContain(privateValue)" }, { "line": 126, "expression": "expect(encoded)" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections runs unchanged AC-50-T01 against the entire actual projection including poisoned private records", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-51-T01", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "files": [ "domain/room.ts", "application/join.ts" ] }, "actions": [ { "type": "scan-domain-imports" } ] }, "assertionGroups": [ { "oraclePaths": [ "forbiddenImports", "checkedSpecifiers.0", "checkedSpecifiers.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "AST runtime/type import guard rejects Next/Supabase/framework/infrastructure edges from domain/application. Real command orchestration stages exactly one domain transition/outbox, returns canonical retry without rerunning decisions, rejects changed digests/stale versions, and preserves committed state on reported rollback. Real room decisions obey conditional capacity conflicts.", "bindings": [ { "file": "tests/architecture/phase1a-bootstrap.test.ts", "title": "keeps domain modules", "sourceTest": "keeps domain modules free of UI, framework, and infrastructure imports", "line": 70, "sourceHash": "cd88f6ed95680ada501c3a498fa1662496dd19f4b0cd256228c0994f4ba9e084", "assertedObservations": [ { "observation": "forbiddenImports", "statements": [ { "line": 77, "expression": "expect(\n forbiddenImports(file, readFileSync(file, \"utf8\"), root),\n file,\n ).toEqual([])" }, { "line": 77, "expression": "expect(\n forbiddenImports(file, readFileSync(file, \"utf8\"), root),\n file,\n )" } ] } ], "executions": [ { "title": "Phase 1A application skeleton and module boundaries keeps domain modules free of UI, framework, and infrastructure imports", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/architecture/phase1a-bootstrap.test.ts", "title": "rejects infrastructure/framework imports", "sourceTest": "rejects infrastructure/framework imports through relative paths, aliases, and dynamic syntax", "line": 83, "sourceHash": "cd88f6ed95680ada501c3a498fa1662496dd19f4b0cd256228c0994f4ba9e084", "assertedObservations": [ { "observation": "forbiddenImports", "statements": [ { "line": 100, "expression": "expect(forbiddenImports(file, statement, root)).toEqual([specifier])" }, { "line": 100, "expression": "expect(forbiddenImports(file, statement, root))" }, { "line": 102, "expression": "expect(forbiddenImports(file, \"import(computed)\", root)).toEqual([\n \"\",\n ])" }, { "line": 102, "expression": "expect(forbiddenImports(file, \"import(computed)\", root))" }, { "line": 105, "expression": "expect(\n forbiddenImports(\n file,\n \"import type { Room } from '../domain/model'\",\n root,\n ),\n ).toEqual([])" }, { "line": 105, "expression": "expect(\n forbiddenImports(\n file,\n \"import type { Room } from '../domain/model'\",\n root,\n ),\n )" }, { "line": 113, "expression": "expect(\n forbiddenImports(\n resolve(root, \"src/domain/example.ts\"),\n \"import x from '../application/ports'\",\n root,\n ),\n ).toEqual([\"../application/ports\"])" }, { "line": 113, "expression": "expect(\n forbiddenImports(\n resolve(root, \"src/domain/example.ts\"),\n \"import x from '../application/ports'\",\n root,\n ),\n )" } ] } ], "executions": [ { "title": "Phase 1A application skeleton and module boundaries rejects infrastructure/framework imports through relative paths, aliases, and dynamic syntax", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/model.ts", "src/application/command.ts", "src/application/rooms.ts", "tests/architecture/import-boundary.ts" ], "fixtureDiscrepancy": [ "Serial transaction port is explicit use-case evidence only; database locking, RLS, uniqueness and transport delivery are separate TR results." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-51-T02", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "repositoryResponses": [ "assigned", "version-conflict-full" ] }, "actions": [ { "type": "assign-capacity-checked-membership" } ] }, "assertionGroups": [ { "oraclePaths": [ "issuedCommands.0.type", "issuedCommands.0.capacity", "results.0", "results.1", "playersExposed", "databaseSerializationClaimed" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "AST runtime/type import guard rejects Next/Supabase/framework/infrastructure edges from domain/application. Real command orchestration stages exactly one domain transition/outbox, returns canonical retry without rerunning decisions, rejects changed digests/stale versions, and preserves committed state on reported rollback. Real room decisions obey conditional capacity conflicts.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/model.ts", "src/application/command.ts", "src/application/rooms.ts", "tests/architecture/import-boundary.ts" ], "fixtureDiscrepancy": [ "Serial transaction port is explicit use-case evidence only; database locking, RLS, uniqueness and transport delivery are separate TR results." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-51-T03", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "commandId": "cmd-1", "priorResult": { "status": "joined", "roomId": "r1" } }, "actions": [ { "type": "handle-duplicate-command" } ] }, "assertionGroups": [ { "oraclePaths": [ "results.0.status", "results.0.roomId", "results.1.status", "results.1.roomId", "domainSideEffectCalls", "databaseUniquenessClaimed" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "AST runtime/type import guard rejects Next/Supabase/framework/infrastructure edges from domain/application. Real command orchestration stages exactly one domain transition/outbox, returns canonical retry without rerunning decisions, rejects changed digests/stale versions, and preserves committed state on reported rollback. Real room decisions obey conditional capacity conflicts.", "bindings": [ { "file": "tests/application/command.test.ts", "title": "runs unchanged AC-51-T03/T04", "sourceTest": "runs unchanged AC-51-T03/T04 oracles against actual replay/rollback orchestration", "line": 244, "sourceHash": "1292fc7e9ab4c06db1e19b1fb640c5e81a6812e6e350ea32bc419184ec157d85", "assertedObservations": [ { "observation": "assertObservation", "statements": [ { "line": 277, "expression": "acOracleScenarios\n .find((entry) => entry.id === \"AC-51-T03\")\n ?.assertObservation({\n results: [first.value, retry.value],\n domainSideEffectCalls: decisions,\n databaseUniquenessClaimed: false,\n })" }, { "line": 293, "expression": "acOracleScenarios\n .find((entry) => entry.id === \"AC-51-T04\")\n ?.assertObservation({\n committed: {\n domainMutations: h.state.version - 4,\n outboxIntents: h.events.length,\n },\n rolledBack: {\n domainMutations: rejected.state.version - 4,\n outboxIntents: rejected.events.length,\n },\n databaseAtomicityClaimed: false,\n })" } ] } ], "executions": [ { "title": "Phase 1B command transaction boundary runs unchanged AC-51-T03/T04 oracles against actual replay/rollback orchestration", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/model.ts", "src/application/command.ts", "src/application/rooms.ts", "tests/architecture/import-boundary.ts" ], "fixtureDiscrepancy": [ "Serial transaction port is explicit use-case evidence only; database locking, RLS, uniqueness and transport delivery are separate TR results." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "primary", "id": "AC-51-T04", "originalSource": "tests/ac-contracts/oracle-scenarios.ts", "originalRequest": { "setup": { "transitions": [ "commit", "rollback" ] }, "actions": [ { "type": "stage-outbox-intent" } ] }, "assertionGroups": [ { "oraclePaths": [ "committed.domainMutations", "committed.outboxIntents", "rolledBack.domainMutations", "rolledBack.outboxIntents", "databaseAtomicityClaimed" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "AST runtime/type import guard rejects Next/Supabase/framework/infrastructure edges from domain/application. Real command orchestration stages exactly one domain transition/outbox, returns canonical retry without rerunning decisions, rejects changed digests/stale versions, and preserves committed state on reported rollback. Real room decisions obey conditional capacity conflicts.", "bindings": [ { "file": "tests/application/command.test.ts", "title": "runs unchanged AC-51-T03/T04", "sourceTest": "runs unchanged AC-51-T03/T04 oracles against actual replay/rollback orchestration", "line": 244, "sourceHash": "1292fc7e9ab4c06db1e19b1fb640c5e81a6812e6e350ea32bc419184ec157d85", "assertedObservations": [ { "observation": "assertObservation", "statements": [ { "line": 277, "expression": "acOracleScenarios\n .find((entry) => entry.id === \"AC-51-T03\")\n ?.assertObservation({\n results: [first.value, retry.value],\n domainSideEffectCalls: decisions,\n databaseUniquenessClaimed: false,\n })" }, { "line": 293, "expression": "acOracleScenarios\n .find((entry) => entry.id === \"AC-51-T04\")\n ?.assertObservation({\n committed: {\n domainMutations: h.state.version - 4,\n outboxIntents: h.events.length,\n },\n rolledBack: {\n domainMutations: rejected.state.version - 4,\n outboxIntents: rejected.events.length,\n },\n databaseAtomicityClaimed: false,\n })" } ] } ], "executions": [ { "title": "Phase 1B command transaction boundary runs unchanged AC-51-T03/T04 oracles against actual replay/rollback orchestration", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/model.ts", "src/application/command.ts", "src/application/rooms.ts", "tests/architecture/import-boundary.ts" ], "fixtureDiscrepancy": [ "Serial transaction port is explicit use-case evidence only; database locking, RLS, uniqueness and transport delivery are separate TR results." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-01-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "profiles": [ { "id": "p1", "icon": "owl" }, { "id": "p2", "icon": "fox" } ], "roomId": "room-a" }, "actions": [ { "type": "join-by-each-path", "payload": { "actorId": "p2" } } ] }, "assertionGroups": [ { "oraclePaths": [ "joins.0", "joins.1", "joins.2", "joins.3", "roster.0.id", "roster.0.icon", "roster.1.id", "roster.1.icon", "sameRoomId" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-02-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "room": { "capacity": 2, "players": [ "p1", "p2" ], "version": 7 } }, "actions": [ { "type": "join-room", "payload": { "actorId": "p3" } } ] }, "assertionGroups": [ { "oraclePaths": [ "configuredCapacity", "before.players.0", "before.players.1", "before.version", "result", "after.players.0", "after.players.1", "after.version" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "fills capacities 2/8/25", "sourceTest": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "line": 129, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "joinRoom", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" } ] }, { "observation": "room", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" }, { "line": 136, "expression": "expect(room).toEqual(before)" }, { "line": 136, "expression": "expect(room)" } ] }, { "observation": "calls", "statements": [ { "line": 154, "expression": "expect(calls).toEqual([[1, capacity]])" }, { "line": 154, "expression": "expect(calls)" } ] }, { "observation": "match.anchorId", "statements": [ { "line": 155, "expression": "expect(match.anchorId).toBe(roster[index - 1])" }, { "line": 155, "expression": "expect(match.anchorId)" } ] } ], "executions": [ { "title": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-04-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "roster": [ "p9", "p3", "p7" ], "category": { "id": "animals", "background": "forest.svg" } }, "actions": [ { "type": "select-anchor-for-values", "payload": { "values": [ 1, 2, 3 ] } }, { "type": "choose-first-category", "payload": { "actorId": "p3", "categoryId": "animals" } } ] }, "assertionGroups": [ { "oraclePaths": [ "mappings.0.value", "mappings.0.player", "mappings.1.value", "mappings.1.player", "mappings.2.value", "mappings.2.player", "firstRound.anchor", "firstRound.category", "firstRound.background" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "fills capacities 2/8/25", "sourceTest": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "line": 129, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "joinRoom", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" } ] }, { "observation": "room", "statements": [ { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok).toBe(false)" }, { "line": 135, "expression": "expect(joinRoom(room, \"extra\", \"player\", null, 0).ok)" }, { "line": 136, "expression": "expect(room).toEqual(before)" }, { "line": 136, "expression": "expect(room)" } ] }, { "observation": "calls", "statements": [ { "line": 154, "expression": "expect(calls).toEqual([[1, capacity]])" }, { "line": 154, "expression": "expect(calls)" } ] }, { "observation": "match.anchorId", "statements": [ { "line": 155, "expression": "expect(match.anchorId).toBe(roster[index - 1])" }, { "line": 155, "expression": "expect(match.anchorId)" } ] } ], "executions": [ { "title": "AC-02/04/24 fills capacities 2/8/25 without mutation on overflow and maps every inclusive anchor index", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/match.test.ts", "title": "rejects non-anchor choices", "sourceTest": "rejects non-anchor choices, binds category art, and consumes only unused eligible pairs", "line": 24, "sourceHash": "bed875c0ab14556abc472428b15c16df6120dde5143e62fe17e8caa7d028f970", "assertedObservations": [ { "observation": "chosen.rounds", "statements": [ { "line": 27, "expression": "expect(chosen.rounds[0].pairId).toBe('c0-p2')" }, { "line": 27, "expression": "expect(chosen.rounds[0].pairId)" }, { "line": 27, "expression": "expect(chosen.rounds[0].stageDueAt).toBe(20100)" }, { "line": 27, "expression": "expect(chosen.rounds[0].stageDueAt)" } ] }, { "observation": "pairId", "statements": [ { "line": 27, "expression": "expect(chosen.rounds[0].pairId).toBe('c0-p2')" }, { "line": 27, "expression": "expect(chosen.rounds[0].pairId)" }, { "line": 28, "expression": "expect(val(chooseCategory(exhausted,'p1','c0',catalog(),random,100,()=> 'round')).rounds[0].pairId).toBe('c0-p0')" }, { "line": 28, "expression": "expect(val(chooseCategory(exhausted,'p1','c0',catalog(),random,100,()=> 'round')).rounds[0].pairId)" } ] } ], "executions": [ { "title": "Phase4A real catalog/match setup rejects non-anchor choices, binds category art, and consumes only unused eligible pairs", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-06-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "playerId": "p1", "target": "cat", "stage": "no-hint" }, "actions": [ { "type": "submit-answer", "payload": { "answer": "cat" } }, { "type": "submit-answer", "payload": { "answer": "cat" } } ] }, "assertionGroups": [ { "oraclePaths": [ "solve.accepted", "solve.scoreAfter", "solve.scoreLocked", "laterSubmission.result", "laterSubmission.scoreAfter" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "independent unsolved answer cases", "sourceTest": "AC-45 supplemental independent unsolved answer cases normalize canonical and approved alternatives, reject fuzzy/punctuation changes and lock later scoring", "line": 160, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "solves.reduce", "statements": [ { "line": 179, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n ).toBe(accepted ? 5 : 0)" }, { "line": 179, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n )" }, { "line": 183, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n ).toBe(accepted ? 5 : 0)" }, { "line": 183, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n )" } ] }, { "observation": "h.answer", "statements": [ { "line": 182, "expression": "expect((await h.answer(\"p1\", input)).ok).toBe(false)" }, { "line": 182, "expression": "expect((await h.answer(\"p1\", input)).ok)" } ] } ], "executions": [ { "title": "AC-45 supplemental independent unsolved answer cases normalize canonical and approved alternatives, reject fuzzy/punctuation changes and lock later scoring", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/real-gameplay.test.ts", "title": "AC-06-T01 real domain/application conformance", "sourceTest": "`${id} real domain/application conformance with unchanged oracle`", "line": 32, "sourceHash": "6ff2ebb1ef686f9cd62acdcd317b630bd7b6cb493aebd1b9be18f8d624a0dc83", "assertedObservations": [ { "observation": "scenario.assertObservation(observed)", "statements": [ { "line": 38, "expression": "scenario.assertObservation(observed)" } ] } ], "executions": [ { "title": "AC-06-T01 real domain/application conformance with unchanged oracle", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "docs/evidence/phase6/gameplay-observations.json", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-09-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "players": [ "p1", "p2" ], "target": "cat", "stage": "hint-1" }, "actions": [ { "type": "submit-answer", "payload": { "player": "p1", "answer": "cat" } }, { "type": "submit-answer", "payload": { "player": "p2", "answer": "dog" } } ] }, "assertionGroups": [ { "oraclePaths": [ "confirmedResults.0.player", "confirmedResults.0.solved", "confirmedResults.1.player", "confirmedResults.1.solved", "nextStage", "hintRecipients.0", "solvedPlayerControls" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/browser/matches.phase4.spec.ts", "title": "actual players and bound observer", "sourceTest": "actual players and bound observer preserve solved hints, scoring and the same uncertain answer retry", "line": 43, "sourceHash": "120a89d6dfd3e782eeb2bfc527a041d762056ff0348f800a775c04088962de63", "assertedObservations": [ { "observation": "visible hints", "statements": [ { "line": 53, "expression": "expect(b.getByRole('list',{name:'Your visible hints'})).toBeVisible()" }, { "line": 53, "expression": "expect(b.getByRole('list',{name:'Your visible hints'}))" }, { "line": 53, "expression": "expect(a.getByRole('list',{name:'Your visible hints'})).toHaveCount(0)" }, { "line": 53, "expression": "expect(a.getByRole('list',{name:'Your visible hints'}))" }, { "line": 53, "expression": "expect(o.getByRole('list',{name:'Your visible hints'})).toHaveCount(0)" }, { "line": 53, "expression": "expect(o.getByRole('list',{name:'Your visible hints'}))" } ] }, { "observation": "Your score is locked", "statements": [ { "line": 50, "expression": "expect(a.getByText(/Your score is locked/).first()).toBeVisible()" }, { "line": 50, "expression": "expect(a.getByText(/Your score is locked/).first())" } ] }, { "observation": "Correct answer", "statements": [ { "line": 53, "expression": "expect(o.getByText('Correct answer:',{exact:false})).toHaveCount(0)" }, { "line": 53, "expression": "expect(o.getByText('Correct answer:',{exact:false}))" }, { "line": 54, "expression": "expect(b.getByText('Correct answer:',{exact:false})).toBeVisible()" }, { "line": 54, "expression": "expect(b.getByText('Correct answer:',{exact:false}))" } ] } ], "executions": [ { "title": "actual players and bound observer preserve solved hints, scoring and the same uncertain answer retry", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-16-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "catalog": [ { "id": "complete", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat", "kitty" ], "category": "animals", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-prompt", "prompt": "", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-target", "prompt": "pet", "target": "", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-hint", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-answer-policy", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [], "category": "animals", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-category", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "", "background": "forest.svg", "approved": true, "enabled": true }, { "id": "missing-background", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "", "approved": true, "enabled": true }, { "id": "unapproved", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "forest.svg", "approved": false, "enabled": true }, { "id": "disabled", "prompt": "pet", "target": "cat", "hints": [ "animal", "feline", "meows" ], "acceptedAnswers": [ "cat" ], "category": "animals", "background": "forest.svg", "approved": true, "enabled": false } ] }, "actions": [ { "type": "query-playable-content", "payload": { "require": [ "prompt", "target", "three-ordered-hints", "accepted-answer-policy", "category", "background", "approved", "enabled" ] } } ] }, "assertionGroups": [ { "oraclePaths": [ "playable.0.id", "playable.0.prompt", "playable.0.target", "playable.0.hints.0", "playable.0.hints.1", "playable.0.hints.2", "playable.0.acceptedAnswers.0", "playable.0.acceptedAnswers.1", "playable.0.category", "playable.0.background", "excluded.0.id", "excluded.0.reason", "excluded.1.id", "excluded.1.reason", "excluded.2.id", "excluded.2.reason", "excluded.3.id", "excluded.3.reason", "excluded.4.id", "excluded.4.reason", "excluded.5.id", "excluded.5.reason", "excluded.6.id", "excluded.6.reason", "excluded.7.id", "excluded.7.reason" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Complete approved records are selectable. Missing prompt, target, ordered hint, policy field or background rejects the catalog; missing category/unapproved records are excluded; disabled/selectable=false pairs are excluded from future pools while historical versions remain resolvable. Canonical target with zero alternatives is valid.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "rejects each incomplete catalog field", "sourceTest": "AC-16 primary/supplemental counterpart rejects each incomplete catalog field and excludes unapproved or disabled future content", "line": 96, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "buildCatalog", "statements": [ { "line": 106, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, ...broken }] as never).ok,\n ).toBe(false)" }, { "line": 106, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, ...broken }] as never).ok,\n )" }, { "line": 109, "expression": "expect(\n value(buildCatalog(catalog.categories, [{ ...pair, categoryId: \"\" }]))\n .pairs,\n ).toEqual([])" }, { "line": 109, "expression": "expect(\n value(buildCatalog(catalog.categories, [{ ...pair, categoryId: \"\" }]))\n .pairs,\n )" }, { "line": 124, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, acceptedAnswers: [] }]).ok,\n ).toBe(true)" }, { "line": 124, "expression": "expect(\n buildCatalog(catalog.categories, [{ ...pair, acceptedAnswers: [] }]).ok,\n )" } ] }, { "observation": "categoryPool", "statements": [ { "line": 120, "expression": "expect(categoryPool(filtered, pair.categoryId).map((p) => p.id)).toEqual([\n pair.id,\n ])" }, { "line": 120, "expression": "expect(categoryPool(filtered, pair.categoryId).map((p) => p.id))" } ] } ], "executions": [ { "title": "AC-16 primary/supplemental counterpart rejects each incomplete catalog field and excludes unapproved or disabled future content", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/catalog.ts", "src/domain/content.ts" ], "fixtureDiscrepancy": [ "Historical acceptedAnswers=[] meant missing policy; production target is canonical and acceptedAnswers lists alternatives, so [] is valid, while undefined/malformed policy is invalid. Historical enabled=false maps to selectable=false. Malformed complete catalog input is rejected as a whole, preventing that record from entering play." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-18-T02", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "caseId": "n1", "state": "rejected", "proposer": "proposer", "originalReviewer": "original", "senior": "senior", "version": 4 }, "actions": [ { "type": "appeal", "payload": { "actor": "proposer", "reason": "Context supplied" } }, { "type": "attempt-appeal", "payload": { "actor": "other" } }, { "type": "attempt-resolution", "payload": { "actor": "junior" } }, { "type": "attempt-resolution", "payload": { "actor": "original" } }, { "type": "resolve-uphold", "payload": { "actor": "senior", "reason": "Evidence insufficient" } }, { "type": "resolve-stale", "payload": { "actor": "senior", "version": 5 } } ] }, "assertionGroups": [ { "oraclePaths": [ "initial.state", "initial.version", "appeal.actor", "appeal.result", "appeal.version", "attempts.0.actor", "attempts.0.result", "attempts.0.version", "attempts.1.actor", "attempts.1.result", "attempts.1.version", "attempts.2.actor", "attempts.2.result", "attempts.2.version", "attempts.3.actor", "attempts.3.outcome", "attempts.3.result", "attempts.3.version", "attempts.4.actor", "attempts.4.expectedVersion", "attempts.4.result", "attempts.4.version", "audit.0.action", "audit.0.actor", "audit.0.reason", "audit.1.action", "audit.1.actor", "audit.1.reason", "publications" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "allows one proposer appeal", "sourceTest": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "line": 22, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "resolveAppeal", "statements": [ { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok)" } ] }, { "observation": "appealCase", "statements": [ { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 23, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 23, "expression": "expect(state.publications)" } ] }, { "observation": "status", "statements": [ { "line": 23, "expression": "expect(state.cases[0].status).toBe(outcome==='uphold'?'rejected':'approved')" }, { "line": 23, "expression": "expect(state.cases[0].status)" } ] } ], "executions": [ { "title": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content-service.test.ts", "title": "commits audited suggestions once", "sourceTest": "AC-19/47 commits audited suggestions once, retains canonical retry, rejects stale and rolls back failed storage", "line": 9, "sourceHash": "fcaa556df70b721e9c8c3c40fdb20ed3c0a6644b63ea086ff27d04ff92085d07", "assertedObservations": [ { "observation": "service.act", "statements": [ { "line": 13, "expression": "expect(await service.act(command,action)).toEqual(result)" }, { "line": 13, "expression": "expect(await service.act(command,action))" }, { "line": 13, "expression": "expect(await service.act({...command,commandId:'stale'},action)).toEqual(failure('CONFLICT'))" }, { "line": 13, "expression": "expect(await service.act({...command,commandId:'stale'},action))" }, { "line": 14, "expression": "expect(await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).toEqual(failure('TEMPORARY_UNAVAILABLE'))" }, { "line": 14, "expression": "expect(await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'}))" }, { "line": 14, "expression": "expect((await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).ok).toBe(true)" }, { "line": 14, "expression": "expect((await service.act(review,{type:'review',caseId:state.cases[0].id,decision:'approve',reason:'Verified'})).ok)" } ] }, { "observation": "audits", "statements": [ { "line": 13, "expression": "expect(audits).toHaveLength(1)" }, { "line": 13, "expression": "expect(audits)" }, { "line": 14, "expression": "expect(audits).toHaveLength(1)" }, { "line": 14, "expression": "expect(audits)" }, { "line": 14, "expression": "expect(audits).toHaveLength(2)" }, { "line": 14, "expression": "expect(audits)" } ] } ], "executions": [ { "title": "AC-19/47 commits audited suggestions once, retains canonical retry, rejects stale and rolls back failed storage", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-19-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "wins": 10, "openSuggestions": 0, "submittedSuggestion": { "id": "new-space", "category": "space", "pairs": [ { "prompt": "night object", "target": "moon" }, { "prompt": "red planet", "target": "mars" }, { "prompt": "ringed planet", "target": "saturn" } ], "moderation": "pending" }, "candidates": [ { "id": "approved-incomplete", "category": "space", "pairs": [ { "prompt": "night object", "target": "moon", "hints": [ "sky", "orb", "night" ], "acceptedAnswers": [ "moon" ], "background": "space.svg" }, { "prompt": "red planet", "target": "mars", "hints": [ "planet", "red", "fourth" ], "acceptedAnswers": [ "mars" ], "background": "space.svg" } ], "approved": true, "enabled": true }, { "id": "approved-complete", "category": "space", "pairs": [ { "prompt": "night object", "target": "moon", "hints": [ "sky", "orb", "night" ], "acceptedAnswers": [ "moon" ], "background": "space.svg" }, { "prompt": "red planet", "target": "mars", "hints": [ "planet", "red", "fourth" ], "acceptedAnswers": [ "mars" ], "background": "space.svg" }, { "prompt": "ringed planet", "target": "saturn", "hints": [ "planet", "rings", "sixth" ], "acceptedAnswers": [ "saturn" ], "background": "space.svg" } ], "approved": true, "enabled": true } ] }, "actions": [ { "type": "suggest-category", "payload": { "id": "new-space" } }, { "type": "suggest-category", "payload": { "id": "second-space" } }, { "type": "query-playability", "payload": { "id": "approved-incomplete" } }, { "type": "query-playability", "payload": { "id": "approved-complete" } } ] }, "assertionGroups": [ { "oraclePaths": [ "first.result", "first.playable", "first.moderation", "first.complete", "secondOpenAttempt", "approvedIncompletePlayable", "approvedCompleteCuratedPlayable" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/application/content.test.ts", "title": "requires ten lifetime wins", "sourceTest": "AC-19 requires ten lifetime wins, exactly three distinct pairs and only one open suggestion", "line": 25, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "suggestCategory", "statements": [ { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),9,proposal,1,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),9,proposal,1,ids,catalog).ok)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,{...proposal,pairs:proposal.pairs.slice(0,2)},1,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,{...proposal,pairs:proposal.pairs.slice(0,2)},1,ids,catalog).ok)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,proposal,2,ids,catalog).ok).toBe(false)" }, { "line": 26, "expression": "expect(suggestCategory(state,player('p1'),10,proposal,2,ids,catalog).ok)" } ] }, { "observation": "playable", "statements": [ { "line": 26, "expression": "expect(state.cases[0].playable).toBe(false)" }, { "line": 26, "expression": "expect(state.cases[0].playable)" } ] }, { "observation": "publications", "statements": [ { "line": 26, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 26, "expression": "expect(state.publications)" } ] } ], "executions": [ { "title": "AC-19 requires ten lifetime wins, exactly three distinct pairs and only one open suggestion", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "activation requires an approved case", "sourceTest": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "line": 28, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "activateCase", "statements": [ { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 29, "expression": "expect(state.publications).toHaveLength(1)" }, { "line": 29, "expression": "expect(state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 29, "expression": "expect(state.cases[0].playable).toBe(true)" }, { "line": 29, "expression": "expect(state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-20-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "oidcSubject": "sub-1", "existingProfile": "p1", "selectedIcon": "owl" }, "actions": [ { "type": "login" }, { "type": "login" }, { "type": "select-icon" } ] }, "assertionGroups": [ { "oraclePaths": [ "firstLogin.profileId", "firstLogin.associationsCreated", "repeatLogin.profileId", "repeatLogin.associationsCreated", "iconSelection.selected", "iconSelection.persisted" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual identity application handles generated guest opt-out, stable browser credential and stable verified issuer/subject member association without guest linking. Browser IndexedDB/cookie clear resets the guest; reload retains preferences; uncertain preference retry has identical payload and one mutation; stale shared-session mutation refreshes identity. Actual OIDC adapter rejects invalid signature, issuer, audience, state, nonce and expiry; invalid callbacks never associate a profile. Private API derives actor and rejects forged displayed identity/roles.", "bindings": [ { "file": "tests/browser/recovery.phase6.spec.ts", "title": "guest clear/reset", "sourceTest": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "line": 164, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "state.guests", "statements": [ { "line": 215, "expression": "expect(state.guests.size).toBe(2)" }, { "line": 215, "expression": "expect(state.guests.size)" } ] }, { "observation": "state.members", "statements": [ { "line": 213, "expression": "expect(state.members.size).toBe(1)" }, { "line": 213, "expression": "expect(state.members.size)" }, { "line": 214, "expression": "expect([...state.members.values()][0]).toBe(member)" }, { "line": 214, "expression": "expect([...state.members.values()][0])" } ] }, { "observation": "state.profiles", "statements": [ { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits).toHaveLength(1)" }, { "line": 179, "expression": "expect(state.profiles.get(firstGuest)?.commits)" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon).toBe(\"owl\")" }, { "line": 209, "expression": "expect(state.profiles.get(secondGuest)?.state.icon)" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon).toBe(\"owl\")" }, { "line": 210, "expression": "expect(state.profiles.get(member)?.state.icon)" } ] }, { "observation": "preferenceBodies", "statements": [ { "line": 178, "expression": "expect(state.preferenceBodies[0]).toEqual(state.preferenceBodies[1])" }, { "line": 178, "expression": "expect(state.preferenceBodies[0])" } ] }, { "observation": "aria-pressed", "statements": [ { "line": 175, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 181, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose fox\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 198, "expression": "expect(\n page.getByRole(\"button\", { name: \"Choose owl\", exact: true }),\n ).toHaveAttribute(\"aria-pressed\", \"true\")" } ] } ], "executions": [ { "title": "AC-20/21/46 guest clear/reset, repeated member entry, uncertain preference retry and changed shared session recover privately", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/identity.ts", "src/infrastructure/oidc.ts", "src/web/identity-panel.tsx", "src/web/guest-credential.ts" ], "fixtureDiscrepancy": [ "Provider discovery/JWKS/token transport and persistence are local fakes; no live account or provider entitlement is claimed. Browser member entry invokes application begin/completeLogin behind this verified-claims port." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-23-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "eligibleRooms": [ "room-a" ], "emptyRooms": [], "staleRoom": "room-a" }, "actions": [ { "type": "quick-match-nonempty" }, { "type": "quick-match-empty" }, { "type": "quick-match-stale" } ] }, "assertionGroups": [ { "oraclePaths": [ "nonempty.selected", "nonempty.result", "empty.selected", "empty.fallback.0", "empty.fallback.1", "staleSelection.result", "staleSelection.fallback.0", "staleSelection.fallback.1" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "oldest-room port contract", "sourceTest": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "line": 22, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "selected", "statements": [ { "line": 89, "expression": "expect(selected).toEqual([\"z\", \"a\", \"a\"])" }, { "line": 89, "expression": "expect(selected)" } ] }, { "observation": "service.quickMatch", "statements": [ { "line": 78, "expression": "expect(await service.quickMatch(\"p2\", \"first\", \"first\")).toEqual(\n success({ joined: \"z\", fallback: [] }),\n )" }, { "line": 78, "expression": "expect(await service.quickMatch(\"p2\", \"first\", \"first\"))" }, { "line": 90, "expression": "expect(await service.quickMatch(\"p4\", \"retry\", \"retry\")).toEqual(\n success({ joined: \"b\", fallback: [] }),\n )" }, { "line": 90, "expression": "expect(await service.quickMatch(\"p4\", \"retry\", \"retry\"))" }, { "line": 93, "expression": "expect(await service.quickMatch(\"p5\", \"empty\", \"empty\")).toEqual(\n success({ joined: null, fallback: [\"browse\", \"create\"] }),\n )" }, { "line": 93, "expression": "expect(await service.quickMatch(\"p5\", \"empty\", \"empty\"))" } ] }, { "observation": "stores.every", "statements": [ { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2)).toBe(true)" }, { "line": 96, "expression": "expect(stores.every((s) => s.state.players.length === 2))" } ] } ], "executions": [ { "title": "AC-23 oldest-room port contract orders age and stable ID; competing quickMatch calls cannot duplicate the final seat", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/recovery.phase6.spec.ts", "title": "empty and lost-seat quick match", "sourceTest": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "line": 320, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "No joinable rooms", "statements": [ { "line": 410, "expression": "expect(\n page.getByText(\n \"No joinable rooms right now. Browse the community board or create a room.\",\n ),\n ).toBeVisible()" }, { "line": 410, "expression": "expect(\n page.getByText(\n \"No joinable rooms right now. Browse the community board or create a room.\",\n ),\n )" } ] }, { "observation": "The room or account changed", "statements": [ { "line": 419, "expression": "expect(\n page.getByText(\n \"The room or account changed. Try again, browse, or create a room.\",\n ),\n ).toBeVisible()" }, { "line": 419, "expression": "expect(\n page.getByText(\n \"The room or account changed. Try again, browse, or create a room.\",\n ),\n )" } ] }, { "observation": "Browse open rooms", "statements": [ { "line": 415, "expression": "expect(\n page.getByRole(\"link\", { name: \"Browse open rooms\" }),\n ).toBeVisible()" }, { "line": 415, "expression": "expect(\n page.getByRole(\"link\", { name: \"Browse open rooms\" }),\n )" } ] } ], "executions": [ { "title": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-24-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "requestedCapacity": 8 }, "actions": [ { "type": "create-room" }, { "type": "project-room" } ] }, "assertionGroups": [ { "oraclePaths": [ "request", "accepted", "storedCapacity", "displayedCapacity" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Populate 2, 8 and 25-player rooms, reject overflow without mutation, populate 25 bound observers and reject observer 26. Competing actual RoomService commands through a serialized port admit only one final player; stale loser conflicts and a fresh full attempt changes nothing. At maximum size, all 25 score exactly once, observers have the corresponding player projection and cannot mutate; room event audience includes all 50 profiles.", "bindings": [ { "file": "tests/browser/recovery.phase6.spec.ts", "title": "empty and lost-seat quick match", "sourceTest": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "line": 320, "sourceHash": "7a2f872f3df37adb8e31b7353c9ccd0c8c4fec7d3b1db3a63e58f380f52731c1", "assertedObservations": [ { "observation": "1 / 8 players", "statements": [ { "line": 427, "expression": "expect(\n page.getByText(\"1 / 8 players · 7 open player slots · waiting\"),\n ).toBeVisible()" }, { "line": 427, "expression": "expect(\n page.getByText(\"1 / 8 players · 7 open player slots · waiting\"),\n )" } ] }, { "observation": "capacity).toBe(8)", "statements": [ { "line": 434, "expression": "expect([...rooms.values()][0].capacity).toBe(8)" } ] } ], "executions": [ { "title": "AC-23/37 empty and lost-seat quick match offer browse/create; service limit creation retries once to confirmed success", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/application/matches.ts" ], "fixtureDiscrepancy": [ "Port fake serialization is use-case evidence only; it does not claim PostgreSQL durability." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-25-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "code": "ABCD", "deepLink": "/rooms/ABCD", "roomId": "room-a", "fullRoomId": "room-full" }, "actions": [ { "type": "join-by-code" }, { "type": "join-by-deep-link" }, { "type": "join-full-room" } ] }, "assertionGroups": [ { "oraclePaths": [ "byCode.roomId", "byCode.membership", "byDeepLink.roomId", "byDeepLink.membership", "full.roomId", "full.membership" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/rooms.test.ts", "title": "rejects a full player join", "sourceTest": "rejects a full player join without mutating the state and binds a separately admitted observer to an active inviter", "line": 12, "sourceHash": "e139ee023ce51c1a9dc6c8bc38cfc4cb89aff8f5c9761b2ffb6b803b4a4053b3", "assertedObservations": [ { "observation": "ROOM_FULL", "statements": [ { "line": 15, "expression": "expect(joinRoom(joined.value,'p3','player',null,3000)).toEqual({ok:false,error:{code:'ROOM_FULL'}})" } ] }, { "observation": "joined.value", "statements": [ { "line": 15, "expression": "expect(joinRoom(joined.value,'p3','player',null,3000)).toEqual({ok:false,error:{code:'ROOM_FULL'}})" }, { "line": 15, "expression": "expect(joinRoom(joined.value,'p3','player',null,3000))" }, { "line": 16, "expression": "expect(joined.value).toEqual(before)" }, { "line": 16, "expression": "expect(joined.value)" }, { "line": 17, "expression": "expect(joinRoom(joined.value,'o1','observer','not-a-player',3000)).toEqual({ok:false,error:{code:'NOT_AUTHORIZED'}})" }, { "line": 17, "expression": "expect(joinRoom(joined.value,'o1','observer','not-a-player',3000))" } ] } ], "executions": [ { "title": "AC-01/02/24–28/50/51 room policy and private projections rejects a full player join without mutating the state and binds a separately admitted observer to an active inviter", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-26-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "openRoom": "open", "fullRoom": "full" }, "actions": [ { "type": "open-deep-link", "payload": { "roomId": "open" } }, { "type": "open-deep-link", "payload": { "roomId": "full" } } ] }, "assertionGroups": [ { "oraclePaths": [ "browseShown", "openRoom.membership", "openRoom.roomId", "fullRoom.membership", "fullRoom.offer", "fullRoom.roomId" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Code, browse, quick-match and signed invitation resolve to the same room; RoomService.join checks capacity and returns one committed membership, conflicting last-seat command and unchanged full rejection. Shared roster contains the selected profile icons. Empty quick-match offers browse/create; a stale lost seat returns CONFLICT with actionable UI recovery. Lookup/selection/signature authority is an injected repository port, with adapter checks recorded separately.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "primary and supplemental counterparts resolve code", "sourceTest": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "line": 339, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "view.roomId", "statements": [ { "line": 422, "expression": "expect(view.roomId).toBe(id)" }, { "line": 422, "expression": "expect(view.roomId)" } ] }, { "observation": "view.players", "statements": [ { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon])).toEqual([\n [\"p1\", \"owl\"],\n [\"p2\", \"fox\"],\n ])" }, { "line": 423, "expression": "expect(view.players.map((p) => [p.id, p.icon]))" } ] }, { "observation": "results.filter", "statements": [ { "line": 452, "expression": "expect(results.filter((r) => r.ok)).toHaveLength(1)" }, { "line": 452, "expression": "expect(results.filter((r) => r.ok))" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n ).toHaveLength(1)" }, { "line": 453, "expression": "expect(\n results.filter((r) => !r.ok && r.error.code === \"CONFLICT\"),\n )" } ] }, { "observation": "store.state", "statements": [ { "line": 440, "expression": "expect(store.state.players).toHaveLength(2)" }, { "line": 440, "expression": "expect(store.state.players)" }, { "line": 456, "expression": "expect(store.state.players).toHaveLength(3)" }, { "line": 456, "expression": "expect(store.state.players)" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n ).toEqual(failure(\"ROOM_FULL\"))" }, { "line": 458, "expression": "expect(\n await service.join(\n {\n ...competing[1],\n commandId: \"retry-current\",\n expectedVersion: store.state.version,\n },\n \"player\",\n null,\n ),\n )" }, { "line": 469, "expression": "expect(store.state).toEqual(snapshot)" }, { "line": 469, "expression": "expect(store.state)" } ] } ], "executions": [ { "title": "AC-01/23/25/26 primary and supplemental counterparts resolve code, browse, quick match and invitation to the same capacity authority", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/browser/rooms.phase2.spec.ts", "title": "signed full-room link", "sourceTest": "AC-26/27 signed full-room link bypasses browsing and keeps the inviter binding through observer entry", "line": 5, "sourceHash": "0c857d31bf7f4b246af2deb2639fc0a3066233e9071c007864d3ce3aef72a2bd", "assertedObservations": [ { "observation": "landing", "statements": [ { "line": 22, "expression": "expect(landing).toEqual([])" }, { "line": 22, "expression": "expect(landing)" } ] }, { "observation": "joins", "statements": [ { "line": 21, "expression": "expect(joins).toHaveLength(1)" }, { "line": 21, "expression": "expect(joins)" }, { "line": 21, "expression": "expect(joins[0]).toMatchObject({action:'join',mode:'observer',inviterId:'p2',expectedProfileId:'observer-1',expectedVersion:1})" }, { "line": 21, "expression": "expect(joins[0])" } ] }, { "observation": "Join as player", "statements": [ { "line": 16, "expression": "expect(page.getByRole('button',{name:'Join as player'})).toHaveCount(0)" }, { "line": 16, "expression": "expect(page.getByRole('button',{name:'Join as player'}))" } ] }, { "observation": "Read-only observer", "statements": [ { "line": 20, "expression": "expect(page.getByText(/Read-only observer · Following Player Elm/)).toBeVisible()" }, { "line": 20, "expression": "expect(page.getByText(/Read-only observer · Following Player Elm/))" } ] } ], "executions": [ { "title": "AC-26/27 signed full-room link bypasses browsing and keeps the inviter binding through observer entry", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/application/rooms.ts", "src/domain/rooms.ts", "src/web/room-screen.tsx", "src/web/play-hub.tsx" ], "fixtureDiscrepancy": [ "The historical four-character ABCD code is replaced by the approved eight-character code. Fixture names, error labels and response field names are projected explicitly rather than copied as raw product DTOs." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-28-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "observer": "o1", "follows": "p2", "priorRevision": 7 }, "actions": [ { "type": "update-player-view", "payload": { "playerId": "p2", "prompt": "pet" } }, { "type": "observer-command", "payload": { "command": "answer" } }, { "type": "observer-command", "payload": { "command": "ready" } } ] }, "assertionGroups": [ { "oraclePaths": [ "inviterView.playerId", "inviterView.revision", "inviterView.prompt", "observerView.follows", "observerView.revision", "observerView.prompt", "commands.0", "commands.1", "scoreRows" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "rejects observers and outsiders", "sourceTest": "rejects observers and outsiders changing readiness or player presence", "line": 46, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "setReady", "statements": [ { "line": 48, "expression": "expect(setReady(room,actor,true,0)).toEqual(failure('NOT_AUTHORIZED'))" }, { "line": 48, "expression": "expect(setReady(room,actor,true,0))" } ] }, { "observation": "presence", "statements": [ { "line": 48, "expression": "expect(presence(room,actor,true,0)).toEqual(failure('NOT_AUTHORIZED'))" }, { "line": 48, "expression": "expect(presence(room,actor,true,0))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 rejects observers and outsiders changing readiness or player presence", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "maximum 25 players", "sourceTest": "AC-02/24/27/28/51 maximum 25 players plus 25 observers keep capacity, locked roster, scoring and perspectives under competing commands", "line": 15, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "observer", "statements": [ { "line": 24, "expression": "expect(joinRoom(room, \"overflow\", \"observer\", \"p1\", 0).ok).toBe(false)" }, { "line": 24, "expression": "expect(joinRoom(room, \"overflow\", \"observer\", \"p1\", 0).ok)" }, { "line": 34, "expression": "expect(room.observers).toHaveLength(25)" }, { "line": 34, "expression": "expect(room.observers)" }, { "line": 67, "expression": "expect(observer).toEqual(player)" }, { "line": 67, "expression": "expect(observer)" }, { "line": 68, "expression": "expect(observer.scorecard).toHaveLength(25)" }, { "line": 68, "expression": "expect(observer.scorecard)" }, { "line": 69, "expression": "expect(observer.standings.every((s) => s.points === 5)).toBe(true)" }, { "line": 69, "expression": "expect(observer.standings.every((s) => s.points === 5))" } ] }, { "observation": "h.act", "statements": [ { "line": 37, "expression": "expect(await h.act({ type: \"choose\", categoryId: \"animals\" }, \"o1\")).toEqual({\n ok: false,\n error: { code: \"NOT_AUTHORIZED\" },\n })" }, { "line": 37, "expression": "expect(await h.act({ type: \"choose\", categoryId: \"animals\" }, \"o1\"))" }, { "line": 70, "expression": "expect((await h.act(action, `o${i}`)).ok).toBe(false)" }, { "line": 70, "expression": "expect((await h.act(action, `o${i}`)).ok)" } ] }, { "observation": "scorecard", "statements": [ { "line": 68, "expression": "expect(observer.scorecard).toHaveLength(25)" }, { "line": 68, "expression": "expect(observer.scorecard)" } ] } ], "executions": [ { "title": "AC-02/24/27/28/51 maximum 25 players plus 25 observers keep capacity, locked roster, scoring and perspectives under competing commands", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/web/lobby-api.test.ts", "title": "returns canonical observer denial", "sourceTest": "returns canonical observer denial and delivers catch-up notifications even when late action is rejected", "line": 21, "sourceHash": "c874ff43ba8727fe72f6c52170d281e4bd2f60b31738fce979e8b408b64dd775", "assertedObservations": [ { "observation": "status).toBe(403)", "statements": [ { "line": 22, "expression": "expect((await POST(request(input),context)).status).toBe(403)" } ] } ], "executions": [ { "title": "returns canonical observer denial and delivers catch-up notifications even when late action is rejected", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-29-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "stage": "answering", "menu": [ "👍", "❤️", "😂", "🎉", "👀" ] }, "actions": [ { "type": "react", "payload": { "emoji": "👍" } }, { "type": "react", "payload": { "emoji": "💣" } } ] }, "assertionGroups": [ { "oraclePaths": [ "stage", "menu.0", "menu.1", "menu.2", "menu.3", "menu.4", "valid.emoji", "valid.broadcast", "invalid.emoji", "invalid.result", "invalid.broadcast", "stateChanged" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Exact 👍 ❤️ 😂 🎉 👀 menu; allowed reaction visible to room players/observers, invalid emoji denied without broadcast. Separate reaction aggregate leaves match, room, answer and score unchanged. Canonical resend does not extend lifetime or repeat event. Rate/expiry timing remains separately identified TR-19 evidence.", "bindings": [ { "file": "tests/application/reactions.test.ts", "title": "permits exactly five emoji", "sourceTest": "AC-29/TR-19 permits exactly five emoji and all player/observer recipients without game mutation", "line": 51, "sourceHash": "ca3a244f02112804193820f9a2c8adc824e672bac92eab6dc6ece90abb15d9cf", "assertedObservations": [ { "observation": "REACTION", "statements": [ { "line": 52, "expression": "expect(REACTIONS).toEqual([\"👍\", \"❤️\", \"😂\", \"🎉\", \"👀\"])" }, { "line": 52, "expression": "expect(REACTIONS)" } ] }, { "observation": "room", "statements": [ { "line": 73, "expression": "expect({ room, match }).toEqual(before)" }, { "line": 73, "expression": "expect({ room, match })" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n )" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n )" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n ).toBe(false)" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n )" } ] }, { "observation": "match", "statements": [ { "line": 73, "expression": "expect({ room, match }).toEqual(before)" }, { "line": 73, "expression": "expect({ room, match })" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 76, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"p1\", emoji, 1000, \"event\")\n .ok,\n )" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n ).toBe(false)" }, { "line": 80, "expression": "expect(\n sendReaction(emptyReactions(\"r\"), room, match, \"o1\", \"👍\", 1000, \"event\")\n .ok,\n )" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n ).toBe(false)" }, { "line": 84, "expression": "expect(\n sendReaction(\n emptyReactions(\"r\"),\n room,\n { ...match, status: \"summary\" },\n \"p1\",\n \"👍\",\n 1000,\n \"event\",\n ).ok,\n )" } ] } ], "executions": [ { "title": "AC-29/TR-19 permits exactly five emoji and all player/observer recipients without game mutation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/reactions.ts", "src/application/reactions.ts", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-32-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "holdout": "p2", "warningStartedAtMs": 0, "warningDueAtMs": 5000 }, "actions": [ { "type": "render-warning", "payload": { "recipient": "p2" } }, { "type": "mark-ready", "payload": { "atMs": 4999 } } ] }, "assertionGroups": [ { "oraclePaths": [ "warning.recipient", "warning.visible", "warning.seconds", "warning.dueAtMs", "readyAtMs", "countdown.started", "countdown.seconds" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/browser/lobby.phase3.spec.ts", "title": "two actual clients ready", "sourceTest": "two actual clients ready, lock their roster and close below two without promoting the observer", "line": 35, "sourceHash": "3b6019fb68b5e4595fdd5218e4c84e0cbc2a6b3fee7519d51ce21790cb7f4306", "assertedObservations": [ { "observation": "Mark ready within 5 seconds", "statements": [ { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'})).toContainText('Mark ready within 5 seconds.')" } ] }, { "observation": "getByRole('alert')", "statements": [ { "line": 43, "expression": "expect(b.getByRole('alert').filter({hasText:'The room will wait for both players.'})).toBeVisible()" }, { "line": 43, "expression": "expect(b.getByRole('alert').filter({hasText:'The room will wait for both players.'}))" }, { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'})).toContainText('Mark ready within 5 seconds.')" }, { "line": 44, "expression": "expect(b.getByRole('alert').filter({hasText:'Mark ready within'}))" }, { "line": 44, "expression": "expect(a.getByRole('alert').filter({hasText:'Mark ready within'})).toHaveCount(0)" }, { "line": 44, "expression": "expect(a.getByRole('alert').filter({hasText:'Mark ready within'}))" }, { "line": 44, "expression": "expect(o.getByRole('alert').filter({hasText:'Mark ready within'})).toHaveCount(0)" }, { "line": 44, "expression": "expect(o.getByRole('alert').filter({hasText:'Mark ready within'}))" } ] }, { "observation": "Roster locked", "statements": [ { "line": 45, "expression": "expect(b.getByText(/Roster locked/)).toBeVisible()" }, { "line": 45, "expression": "expect(b.getByText(/Roster locked/))" } ] } ], "executions": [ { "title": "two actual clients ready, lock their roster and close below two without promoting the observer", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "starts a five-second warning", "sourceTest": "starts a five-second warning only at all-but-one, then locks immediately on final ready", "line": 51, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "holdoutDueAt", "statements": [ { "line": 52, "expression": "expect(room.holdoutDueAt).toBeNull()" }, { "line": 52, "expression": "expect(room.holdoutDueAt)" }, { "line": 53, "expression": "expect(room.holdoutDueAt).toBe(5200)" }, { "line": 53, "expression": "expect(room.holdoutDueAt)" } ] }, { "observation": "countdownDueAt", "statements": [ { "line": 55, "expression": "expect(room.countdownDueAt).toBe(15199)" }, { "line": 55, "expression": "expect(room.countdownDueAt)" } ] }, { "observation": "expireLobby", "statements": [ { "line": 56, "expression": "expect(expireLobby(room,stale,5200)).toEqual(failure('CONFLICT'))" }, { "line": 56, "expression": "expect(expireLobby(room,stale,5200))" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 starts a five-second warning only at all-but-one, then locks immediately on final ready", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-32-T02", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "holdout": "p3", "startedAtMs": 0, "dueAtMs": 5000, "players": [ "p1", "p2", "p3" ] }, "actions": [ { "type": "fire-warning-deadline", "payload": { "atMs": 5000 } } ] }, "assertionGroups": [ { "oraclePaths": [ "deadline.startedAtMs", "deadline.dueAtMs", "deadline.firedAtMs", "removed", "roster.0", "roster.1", "countdownLocked" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/application/lobby.test.ts", "title": "removes a holdout in larger", "sourceTest": "removes a holdout in larger rooms and locks remaining roster without promoting", "line": 58, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "expireLobby", "statements": [ { "line": 60, "expression": "expect(expireLobby(room,job(room,'holdout'),4999)).toEqual(failure('CONFLICT'))" }, { "line": 60, "expression": "expect(expireLobby(room,job(room,'holdout'),4999))" } ] }, { "observation": "next.players", "statements": [ { "line": 61, "expression": "expect(next.players.map(p=>p.profileId)).toEqual(['p1','p2'])" }, { "line": 61, "expression": "expect(next.players.map(p=>p.profileId))" } ] }, { "observation": "next.observers", "statements": [ { "line": 61, "expression": "expect(next.observers).toHaveLength(1)" }, { "line": 61, "expression": "expect(next.observers)" } ] }, { "observation": "next.status", "statements": [ { "line": 61, "expression": "expect(next.status).toBe('countdown')" }, { "line": 61, "expression": "expect(next.status)" } ] }, { "observation": "next.countdownDueAt", "statements": [ { "line": 61, "expression": "expect(next.countdownDueAt).toBe(15000)" }, { "line": 61, "expression": "expect(next.countdownDueAt)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 removes a holdout in larger rooms and locks remaining roster without promoting", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-33-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "initialPlayers": [ "p1", "p2" ], "resetMs": 30000, "eventClock": [ { "event": "ready-change", "changedAtMs": 1000, "oldDueAtMs": 30000, "newDueAtMs": 31000, "roster": [ "p1", "p2" ] }, { "event": "player-join", "changedAtMs": 32000, "oldDueAtMs": 61000, "newDueAtMs": 62000, "roster": [ "p1", "p2", "p3" ] }, { "event": "player-leave", "changedAtMs": 63000, "oldDueAtMs": 92000, "newDueAtMs": 93000, "roster": [ "p1", "p3" ] }, { "event": "observer-join", "changedAtMs": 94000, "oldDueAtMs": 123000, "newDueAtMs": 124000, "roster": [ "p1", "p3" ] }, { "event": "observer-leave", "changedAtMs": 125000, "oldDueAtMs": 154000, "newDueAtMs": 155000, "roster": [ "p1", "p3" ] } ] }, "actions": [ { "type": "change-ready", "payload": { "player": "p1", "atMs": 1000 } }, { "type": "join-player", "payload": { "player": "p3", "atMs": 32000 } }, { "type": "leave-player", "payload": { "player": "p2", "atMs": 63000 } }, { "type": "join-observer", "payload": { "observer": "o1", "atMs": 94000 } }, { "type": "leave-observer", "payload": { "observer": "o1", "atMs": 125000 } }, { "type": "fire-each-old-and-new-deadline" }, { "type": "start-countdown", "payload": { "atMs": 156000 } }, { "type": "fire-stale-idle-callback", "payload": { "atMs": 185000 } } ] }, "assertionGroups": [ { "oraclePaths": [ "resets.0.event", "resets.0.oldDeadlinePrompt", "resets.0.newDeadlineRecipients.0", "resets.0.newDeadlineRecipients.1", "resets.1.event", "resets.1.oldDeadlinePrompt", "resets.1.newDeadlineRecipients.0", "resets.1.newDeadlineRecipients.1", "resets.1.newDeadlineRecipients.2", "resets.2.event", "resets.2.oldDeadlinePrompt", "resets.2.newDeadlineRecipients.0", "resets.2.newDeadlineRecipients.1", "resets.3.event", "resets.3.oldDeadlinePrompt", "resets.3.newDeadlineRecipients.0", "resets.3.newDeadlineRecipients.1", "resets.4.event", "resets.4.oldDeadlinePrompt", "resets.4.newDeadlineRecipients.0", "resets.4.newDeadlineRecipients.1", "countdownStarted.staleIdleCallbackPrompts", "oncePerVersion" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Full pre-countdown departure compacts survivors and promotes oldest observer last, with stable profile ID for equal arrival time. Empty queue leaves a slot. Concurrent/stale commands conflict canonically. Final ready locks a 10-second roster; locked departures never substitute and fewer than two originals closes at expiry. Five-second warning removes holdout only above two; two-player holdout remains. Ready/deadline races and stale jobs cannot apply twice. All five readiness/player/observer changes reset the 30-second prompt; old jobs are silent, current roster prompts once and countdown suppresses idle. Explicit loss, 45-second detection, full 120-second grace and late catch-up preserve original timing; browser reconnect restores the same seat and expired perspective disappears.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "all five membership/readiness changes", "sourceTest": "AC-33 supplemental all five membership/readiness changes silence old idle deadlines, prompt the current roster once and suppress prompts after lock", "line": 473, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "expireLobby", "statements": [ { "line": 503, "expression": "expect(expireLobby(room, old, 30000).ok).toBe(false)" }, { "line": 503, "expression": "expect(expireLobby(room, old, 30000).ok)" }, { "line": 515, "expression": "expect(expireLobby(prompted, fresh, 31001).ok).toBe(false)" }, { "line": 515, "expression": "expect(expireLobby(prompted, fresh, 31001).ok)" }, { "line": 520, "expression": "expect(expireLobby(locked, fresh, 31000).ok).toBe(false)" }, { "line": 520, "expression": "expect(expireLobby(locked, fresh, 31000).ok)" } ] }, { "observation": "fresh.dueAt", "statements": [ { "line": 509, "expression": "expect(fresh.dueAt).toBe(31000)" }, { "line": 509, "expression": "expect(fresh.dueAt)" } ] }, { "observation": "prompted.players", "statements": [ { "line": 512, "expression": "expect(prompted.players.map((p) => p.profileId)).toEqual(\n room.players.map((p) => p.profileId),\n )" }, { "line": 512, "expression": "expect(prompted.players.map((p) => p.profileId))" } ] }, { "observation": "locked.status", "statements": [ { "line": 519, "expression": "expect(locked.status).toBe(\"countdown\")" }, { "line": 519, "expression": "expect(locked.status)" } ] } ], "executions": [ { "title": "AC-33 supplemental all five membership/readiness changes silence old idle deadlines, prompt the current roster once and suppress prompts after lock", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/lobby.test.ts", "title": "prompts once after 30s", "sourceTest": "prompts once after 30s, preserves the interval across presence, and resets on membership/readiness", "line": 79, "sourceHash": "11341fc834cda61d1c20481b073e5964d2796b1e00476f7914a330c2cb47d7bc", "assertedObservations": [ { "observation": "dueAt", "statements": [ { "line": 81, "expression": "expect(job(room,'idle').dueAt).toBe(30000)" }, { "line": 81, "expression": "expect(job(room,'idle').dueAt)" }, { "line": 85, "expression": "expect(job(room,'idle').dueAt).toBe(62000)" }, { "line": 85, "expression": "expect(job(room,'idle').dueAt)" } ] }, { "observation": "expireLobby", "statements": [ { "line": 81, "expression": "expect(expireLobby(room,old,30000)).toEqual(failure('CONFLICT'))" }, { "line": 81, "expression": "expect(expireLobby(room,old,30000))" }, { "line": 82, "expression": "expect(expireLobby(room,job(room,'idle'),29999)).toEqual(failure('CONFLICT'))" }, { "line": 82, "expression": "expect(expireLobby(room,job(room,'idle'),29999))" } ] }, { "observation": "idlePromptedVersion", "statements": [ { "line": 83, "expression": "expect(room.idlePromptedVersion).not.toBeNull()" }, { "line": 83, "expression": "expect(room.idlePromptedVersion)" }, { "line": 84, "expression": "expect(room.idlePromptedVersion).not.toBeNull()" }, { "line": 84, "expression": "expect(room.idlePromptedVersion)" } ] } ], "executions": [ { "title": "Phase 3 actual lobby policy: AC-30–33 and TR-09/10 prompts once after 30s, preserves the interval across presence, and resets on membership/readiness", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/lobby.ts", "src/application/lobby.ts", "src/web/room-screen.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-38-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "complete": { "activeAcIds": [ "AC-01", "AC-02" ], "mappings": [ { "acId": "AC-01", "testIds": [ "AC-01-T01" ] }, { "acId": "AC-02", "testIds": [ "AC-02-T01" ] } ], "existingTestIds": [ "AC-01-T01", "AC-02-T01" ], "mockValidationRecords": [ { "testId": "AC-01-T01", "setup": { "room": "open" }, "actions": [ { "type": "join" } ], "observation": { "result": "joined" }, "assertions": [ "membership is player" ], "status": "passing" }, { "testId": "AC-02-T01", "setup": { "room": "full" }, "actions": [ { "type": "join" } ], "observation": { "result": "no-slot" }, "assertions": [ "roster unchanged" ], "status": "passing" } ] }, "mutations": [ { "name": "missing-ac", "removeMappingFor": "AC-02" }, { "name": "missing-test", "mapAc": "AC-02", "testId": "AC-02-T99" }, { "name": "missing-record", "removeRecordFor": "AC-02-T01" }, { "name": "incomplete-record", "testId": "AC-02-T01", "assertions": [] }, { "name": "planned-record", "testId": "AC-02-T01", "status": "planned" } ] }, "actions": [ { "type": "validate-complete-manifest" }, { "type": "apply-and-validate-each-mutation" } ] }, "assertionGroups": [ { "oraclePaths": [ "controls.0.fixture", "controls.0.result", "controls.1.fixture", "controls.1.result", "controls.2.fixture", "controls.2.result", "controls.3.fixture", "controls.3.result", "controls.4.fixture", "controls.4.result", "controls.5.fixture", "controls.5.result", "controls.6.fixture", "controls.6.result", "deploymentEvidenceRequired" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "45 active IDs/67 primary IDs/27 supplemental clauses are mapped to existing executable evidence. Missing AC/test/record/assertions, planned-only records, duplicate IDs and empty active set are rejected; all named semantic controls remain unchanged. The final manifest binds each oracle leaf to counterpart evidence and fixture discrepancies.", "bindings": [ { "file": "tests/traceability/ac-inventory.test.ts", "title": "validates concrete manifest", "sourceTest": "validates concrete manifest mappings and mock-validation records", "line": 38, "sourceHash": "f702fc61234b59a6ab01b226772d3d9a7179ab355db933076b0c67ccb87a8701", "assertedObservations": [ { "observation": "validateTraceabilityManifest", "statements": [ { "line": 48, "expression": "expect(validateTraceabilityManifest(complete)).toEqual({ valid: true })" }, { "line": 48, "expression": "expect(validateTraceabilityManifest(complete))" }, { "line": 51, "expression": "expect(validateTraceabilityManifest(planned)).toEqual({ valid: false, code: 'incomplete-record' })" }, { "line": 51, "expression": "expect(validateTraceabilityManifest(planned))" }, { "line": 52, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: complete.mappings.slice(0, 1) })).toEqual({ valid: false, code: 'missing-ac' })" }, { "line": 52, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: complete.mappings.slice(0, 1) }))" }, { "line": 53, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: [{ acId: 'AC-01', testIds: ['AC-01-T99'] }, complete.mappings[1]] })).toEqual({ valid: false, code: 'missing-test' })" }, { "line": 53, "expression": "expect(validateTraceabilityManifest({ ...complete, mappings: [{ acId: 'AC-01', testIds: ['AC-01-T99'] }, complete.mappings[1]] }))" }, { "line": 54, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: complete.mockValidationRecords.slice(0, 1) })).toEqual({ valid: false, code: 'missing-record' })" }, { "line": 54, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: complete.mockValidationRecords.slice(0, 1) }))" }, { "line": 55, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: [{ ...complete.mockValidationRecords[0], assertions: [] }, complete.mockValidationRecords[1]] })).toEqual({ valid: false, code: 'incomplete-record' })" }, { "line": 55, "expression": "expect(validateTraceabilityManifest({ ...complete, mockValidationRecords: [{ ...complete.mockValidationRecords[0], assertions: [] }, complete.mockValidationRecords[1]] }))" }, { "line": 56, "expression": "expect(validateTraceabilityManifest({ activeAcIds: [], mappings: [], existingTestIds: [], mockValidationRecords: [] })).toEqual({ valid: false, code: 'empty-active-set' })" }, { "line": 56, "expression": "expect(validateTraceabilityManifest({ activeAcIds: [], mappings: [], existingTestIds: [], mockValidationRecords: [] }))" } ] } ], "executions": [ { "title": "AC test inventory traceability (AC-38) validates concrete manifest mappings and mock-validation records", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "tests/traceability/inventory-validator.ts" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-39-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "externalAssets": [ "Recursive font" ] }, "actions": [ { "type": "validate-external-asset-attribution" } ] }, "assertionGroups": [ { "oraclePaths": [ "externalAssets.0.name", "externalAssets.0.purpose", "externalAssets.0.license", "externalAssets.0.upstream", "externalAssets.0.noticePresent" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Installed direct package versions/licenses match actual lock and purpose/upstream attribution rows. Original SVG Recursive font copyright/OFL text and Unicode source/license/hash are preserved; six background SVGs are original project artwork.", "bindings": [ { "file": "tests/ac-contracts/oracle-supplemental-validation.test.ts", "title": "validates complete GPL", "sourceTest": "AC-42 validates complete GPL and the actual Recursive-bearing SVG notice", "line": 91, "sourceHash": "7fcb507554fc0c71abaa131d3e9c45f3051628610aa0404aa2fd090f81cfc3f2", "assertedObservations": [ { "observation": "validateReleaseNoticesZip", "statements": [ { "line": 100, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(baseArchive), expected)).toBe(true)" }, { "line": 100, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(baseArchive), expected))" }, { "line": 101, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, LICENSE: 'GNU GENERAL PUBLIC LICENSE\\nVersion 2, June 1991' }), expected)).toBe(false)" }, { "line": 101, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, LICENSE: 'GNU GENERAL PUBLIC LICENSE\\nVersion 2, June 1991' }), expected))" }, { "line": 102, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/ATTRIBUTIONS.md': 'Recursive font — OFL-1.1' }), expected)).toBe(false)" }, { "line": 102, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/ATTRIBUTIONS.md': 'Recursive font — OFL-1.1' }), expected))" }, { "line": 104, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(withoutAsset), expected)).toBe(false)" }, { "line": 104, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive(withoutAsset), expected))" }, { "line": 107, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.replace(removed, '') }), expected)).toBe(false)" }, { "line": 107, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.replace(removed, '') }), expected))" }, { "line": 109, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.slice(0, assetText.indexOf('TERMINATION')) }), expected)).toBe(false)" }, { "line": 109, "expression": "expect(await validateReleaseNoticesZip(await createZipArchive({ ...baseArchive, 'docs/word_association_round_flow.svg': assetText.slice(0, assetText.indexOf('TERMINATION')) }), expected))" } ] } ], "executions": [ { "title": "AC-42 validates complete GPL and the actual Recursive-bearing SVG notice", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "package.json", "package-lock.json", "docs/ATTRIBUTIONS.md", "LICENSE", "third_party/unicode/CaseFolding-18.0.0.txt" ], "fixtureDiscrepancy": [ "Dependency versions and asset inventory are the actual locked project, not the historical example packages." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-43-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "modes": [ "normal", "empty", "unavailable" ], "version": "2.6" }, "actions": [ { "type": "render-each-landing-mode" } ] }, "assertionGroups": [ { "oraclePaths": [ "normal.rules.0", "normal.rules.1", "normal.rules.2", "normal.version", "normal.updated", "empty.leaderboard", "empty.lobbies", "unavailable.leaderboard", "unavailable.lobbies" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/web/landing.test.ts", "title": "renders rules, ranking and rooms", "sourceTest": "renders rules, ranking and rooms without sign-in and distinguishes a successful empty query", "line": 88, "sourceHash": "ffcc2c27b12a0bb2dcb08def7cbc6f7e33156214ce5ad8956fd9c95a284725df", "assertedObservations": [ { "observation": "screen", "statements": [ { "line": 94, "expression": "expect(screen.getByRole(\"heading\", { name: \"Leaderboard\" })).toBeDefined()" }, { "line": 94, "expression": "expect(screen.getByRole(\"heading\", { name: \"Leaderboard\" }))" }, { "line": 95, "expression": "expect(screen.getByRole(\"heading\", { name: \"Open lobbies\" })).toBeDefined()" }, { "line": 95, "expression": "expect(screen.getByRole(\"heading\", { name: \"Open lobbies\" }))" }, { "line": 96, "expression": "expect(screen.getByRole(\"heading\", { name: \"How to play\" })).toBeDefined()" }, { "line": 96, "expression": "expect(screen.getByRole(\"heading\", { name: \"How to play\" }))" }, { "line": 97, "expression": "expect(screen.getByText(\"No games yet\")).toBeDefined()" }, { "line": 97, "expression": "expect(screen.getByText(\"No games yet\"))" }, { "line": 98, "expression": "expect(screen.getByText(\"No joinable rooms\")).toBeDefined()" }, { "line": 98, "expression": "expect(screen.getByText(\"No joinable rooms\"))" } ] } ], "executions": [ { "title": "AC-43 anonymous landing states renders rules, ranking and rooms without sign-in and distinguishes a successful empty query", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/web/landing.test.ts", "title": "shows recoverable unavailable", "sourceTest": "shows recoverable unavailable states rather than reporting empty rankings on failure", "line": 101, "sourceHash": "ffcc2c27b12a0bb2dcb08def7cbc6f7e33156214ce5ad8956fd9c95a284725df", "assertedObservations": [ { "observation": "screen", "statements": [ { "line": 108, "expression": "expect(screen.getAllByText(\"Try again later\")).toHaveLength(2)" }, { "line": 108, "expression": "expect(screen.getAllByText(\"Try again later\"))" }, { "line": 110, "expression": "expect(screen.queryByText(\"No games yet\")).toBeNull()" }, { "line": 110, "expression": "expect(screen.queryByText(\"No games yet\"))" }, { "line": 111, "expression": "expect(\n screen.getByRole(\"button\", { name: \"Refresh public information\" }),\n ).toBeDefined()" }, { "line": 111, "expression": "expect(\n screen.getByRole(\"button\", { name: \"Refresh public information\" }),\n )" } ] } ], "executions": [ { "title": "AC-43 anonymous landing states shows recoverable unavailable states rather than reporting empty rankings on failure", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-43-T02", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "profiles": [ { "id": "wins", "kind": "member", "public": true, "wins": 5, "points": 1, "earliestWin": 50 }, { "id": "points", "kind": "member", "public": true, "wins": 4, "points": 30, "earliestWin": 100 }, { "id": "early-a", "kind": "member", "public": true, "wins": 4, "points": 20, "earliestWin": 20 }, { "id": "early-b", "kind": "member", "public": true, "wins": 4, "points": 20, "earliestWin": 20 }, { "id": "opted-out", "kind": "member", "public": false, "wins": 99, "points": 99, "earliestWin": 1 }, { "id": "guest-not-opted-in", "kind": "guest", "optedIn": false, "wins": 99, "points": 99, "earliestWin": 1 } ] }, "actions": [ { "type": "rank-public-leaderboard" } ] }, "assertionGroups": [ { "oraclePaths": [ "rows.0.id", "rows.0.wins", "rows.0.points", "rows.0.earliestWin", "rows.1.id", "rows.1.wins", "rows.1.points", "rows.1.earliestWin", "rows.2.id", "rows.2.wins", "rows.2.points", "rows.2.earliestWin", "rows.3.id", "rows.3.wins", "rows.3.points", "rows.3.earliestWin", "stableTieOrder.0", "stableTieOrder.1", "excluded.0", "excluded.1", "refreshSeconds" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Anonymous normal/empty/unavailable landing renders real versioned rules. Ranking sorts lifetime wins, points, earliest win and stable ID, filtering opt-outs including guests; generated aliases only. Exclusion IDs are derived from actual input/output difference, and observed repository call clocks establish 5-minute ranking and 15-second lobby refresh. Joinable summaries expose exact counts/capacity, waiting state and room age, with allowlisted DTOs excluding private names/codes/targets/provider records.", "bindings": [ { "file": "tests/application/public-projections.test.ts", "title": "orders lifetime wins", "sourceTest": "orders lifetime wins, points, earliest win and stable ID while excluding opt-outs", "line": 51, "sourceHash": "6fb41708604c88caa41eb0680c9a06046bcc25b08d4d7e6869214c2c66d84a18", "assertedObservations": [ { "observation": "calls", "statements": [ { "line": 81, "expression": "expect(calls).toEqual([0])" }, { "line": 81, "expression": "expect(calls)" }, { "line": 84, "expression": "expect(calls).toHaveLength(2)" }, { "line": 84, "expression": "expect(calls)" }, { "line": 85, "expression": "acOracleScenarios\n .find((scenario) => scenario.id === \"AC-43-T02\")\n ?.assertObservation({\n rows: ranked.map((entry) => ({\n alias: entry.alias,\n profileId: entry.id,\n })),\n excludedProfileIds: inputs\n .filter((input) => !ranked.some((row) => row.id === input.id))\n .map((input) => input.id),\n refreshAtMs: calls[1],\n })" } ] }, { "observation": "rankPublicProfiles", "statements": [ { "line": 97, "expression": "expect(\n rankPublicProfiles([profile(\"b\"), profile(\"a\")]).map((entry) => entry.id),\n ).toEqual([\"a\", \"b\"])" }, { "line": 97, "expression": "expect(\n rankPublicProfiles([profile(\"b\"), profile(\"a\")]).map((entry) => entry.id),\n )" }, { "line": 100, "expression": "expect(\n rankPublicProfiles([\n profile(\"points\", { points: 21 }),\n profile(\"wins\", { wins: 4 }),\n ]).map((entry) => entry.id),\n ).toEqual([\"wins\", \"points\"])" }, { "line": 100, "expression": "expect(\n rankPublicProfiles([\n profile(\"points\", { points: 21 }),\n profile(\"wins\", { wins: 4 }),\n ]).map((entry) => entry.id),\n )" } ] } ], "executions": [ { "title": "Phase 2A anonymous public projections orders lifetime wins, points, earliest win and stable ID while excluding opt-outs", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/public-service.test.ts", "title": "refreshes lobbies", "sourceTest": "refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "line": 12, "sourceHash": "6e93213aad39b39d687b7bf4627c0fe42268c56d953e9e69c2e29a549a2e3fc8", "assertedObservations": [ { "observation": "repository.lobbies", "statements": [ { "line": 19, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(1)" }, { "line": 19, "expression": "expect(repository.lobbies)" }, { "line": 22, "expression": "expect(repository.lobbies).toHaveBeenCalledTimes(2)" }, { "line": 22, "expression": "expect(repository.lobbies)" } ] }, { "observation": "repository.leaderboard", "statements": [ { "line": 23, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(1)" }, { "line": 23, "expression": "expect(repository.leaderboard)" }, { "line": 26, "expression": "expect(repository.leaderboard).toHaveBeenCalledTimes(2)" }, { "line": 26, "expression": "expect(repository.leaderboard)" }, { "line": 27, "expression": "expect(repository.leaderboard).toHaveBeenLastCalledWith({ offset: 0, limit: 25, revision: null })" }, { "line": 27, "expression": "expect(repository.leaderboard)" } ] } ], "executions": [ { "title": "TR-30 bounded public reads and cache ownership refreshes lobbies at 15 seconds and rankings at 5 minutes independent of identity", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/application/public-projections.ts", "src/application/public-service.ts", "src/web/landing-board.tsx" ], "fixtureDiscrepancy": [ "Rules include the original three concise instructions plus approved eight-round/scoring instructions. Production DTOs and cache behavior are asserted separately where historical combined fixture DTOs differ." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-44-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "states": [ "icon-selected", "solved", "score-change" ], "inputs": [ "touch", "keyboard" ] }, "actions": [ { "type": "render-accessible-indicators" }, { "type": "invoke-primary-action", "payload": { "input": "touch" } }, { "type": "invoke-primary-action", "payload": { "input": "keyboard" } } ] }, "assertionGroups": [ { "oraclePaths": [ "indicators.0.state", "indicators.0.text", "indicators.0.icon", "indicators.1.state", "indicators.1.text", "indicators.1.icon", "indicators.2.state", "indicators.2.text", "indicators.2.icon", "primaryActions.0.input", "primaryActions.0.result", "primaryActions.1.input", "primaryActions.1.result", "focusVisible", "contrastRatio" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Computed gameplay foreground/background RGBA ratios are retained unrounded; compositing over black and white gives conservative artwork bounds ≥4.5. Four viewports verify solid 3px visible keyboard focus, ≥44px primary button, Tab/Enter and touch operation. Icon selection is conveyed by accessible aria-pressed plus named icon; solved check/text and +3 points text convey status without color.", "bindings": [ { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "keyboard and touch", "sourceTest": "`AC-15/44 counterparts ${viewport.width}x${viewport.height} keyboard and touch`", "line": 149, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "scrollWidth", "statements": [ { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n ).toBe(true)" }, { "line": 165, "expression": "expect(\n await page.evaluate(\n () => document.documentElement.scrollWidth <= innerWidth,\n ),\n )" } ] }, { "observation": "sample.ratios", "statements": [ { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n ).toBeGreaterThanOrEqual(4.5)" }, { "line": 238, "expression": "expect(\n Math.min(...sample.ratios),\n sample.selector,\n )" } ] }, { "observation": "measured.focus", "statements": [ { "line": 242, "expression": "expect(measured.focus.style).toBe(\"solid\")" }, { "line": 242, "expression": "expect(measured.focus.style)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width)).toBeGreaterThanOrEqual(3)" }, { "line": 243, "expression": "expect(Number.parseFloat(measured.focus.width))" } ] }, { "observation": "measured.buttonHeight", "statements": [ { "line": 244, "expression": "expect(measured.buttonHeight).toBeGreaterThanOrEqual(44)" }, { "line": 244, "expression": "expect(measured.buttonHeight)" } ] }, { "observation": "Solved", "statements": [ { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n ).toBeVisible()" }, { "line": 247, "expression": "expect(\n page.getByText(\n \"✓ Solved. Your score is locked; later hints stay private to unsolved players.\",\n ),\n )" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/)).toBeVisible()" }, { "line": 252, "expression": "expect(page.getByText(/Solved · \\+3 points/))" } ] } ], "executions": [ { "title": "AC-15/44 counterparts 320x568 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 568x320 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 390x844 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" }, { "title": "AC-15/44 counterparts 844x390 keyboard and touch", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] }, { "file": "tests/browser/accessibility.phase6.spec.ts", "title": "chosen icon persists", "sourceTest": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "line": 281, "sourceHash": "f4a9802e241485b097540761538955c31b6f90a0ae78d3974fe5498d94a89da0", "assertedObservations": [ { "observation": "aria-pressed", "statements": [ { "line": 296, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" }, { "line": 298, "expression": "expect(selected).toHaveAttribute(\"aria-pressed\", \"true\")" } ] }, { "observation": "frog", "statements": [ { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 301, "expression": "expect(\n page\n .locator(\".roster li\")\n .filter({ hasText: \"Guest Oak\" })\n .getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 311, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 325, "expression": "expect(\n playerRow(scorecard).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] }, { "observation": "final", "statements": [ { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n ).toBeVisible()" }, { "line": 347, "expression": "expect(\n playerRow(final).getByRole(\"img\", { name: \"frog\", exact: true }),\n )" } ] } ], "executions": [ { "title": "AC-03/44 chosen icon persists through reload, room, live scorecard, summary and final standings", "status": "passed", "artifact": "docs/evidence/phase6/browser.json" } ] } ] } ], "productionEntryPoints": [ "src/app/globals.css", "src/web/identity-panel.tsx", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Original contrastRatio exactly 4.5 is a threshold fixture, not an expected measurement. Actual minimum is retained, never capped. Historical literal “Owl selected” maps to accessible named icon and aria-pressed=true; score uses text +3 points without requiring a decorative arrow." ], "evidenceTier": "Actual production decisions and Chromium UI, injected external ports", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-45-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "policy": { "normalization": [ "NFKC", "trim", "collapse-internal-whitespace", "case-fold" ], "punctuation": "preserved", "fuzzyMatching": false }, "cases": [ { "player": "p1", "input": " CAT ", "target": "cat" }, { "player": "p2", "input": " KITTY ", "target": "cat", "approvedAlternatives": [ "kitty" ] }, { "player": "p3", "input": "sea lion", "target": "sea lion" }, { "player": "p4", "input": "spider man", "target": "spider-man" }, { "player": "p5", "input": "cta", "target": "cat" }, { "player": "p6", "input": "kitten", "target": "cat", "approvedAlternatives": [ "kitty" ] } ] }, "actions": [ { "type": "submit-independent-unsolved-cases" }, { "type": "retry-after-solve", "payload": { "player": "p1" } } ] }, "assertionGroups": [ { "oraclePaths": [ "policy.normalization.0", "policy.normalization.1", "policy.normalization.2", "policy.normalization.3", "policy.punctuation", "policy.fuzzyMatching", "cases.0.name", "cases.0.input", "cases.0.target", "cases.0.accepted", "cases.0.scoreDelta", "cases.1.name", "cases.1.input", "cases.1.target", "cases.1.approvedAlternatives.0", "cases.1.accepted", "cases.1.scoreDelta", "cases.2.name", "cases.2.input", "cases.2.target", "cases.2.accepted", "cases.2.scoreDelta", "cases.3.name", "cases.3.input", "cases.3.target", "cases.3.accepted", "cases.3.scoreDelta", "cases.4.name", "cases.4.input", "cases.4.target", "cases.4.accepted", "cases.4.scoreDelta", "cases.5.name", "cases.5.input", "cases.5.target", "cases.5.approvedAlternatives.0", "cases.5.accepted", "cases.5.scoreDelta", "retryAfterSolve.result", "retryAfterSolve.scoreDelta" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Actual MatchService commands and domain projections provide anchor authority, 5/3/2/1 scoring once, all-unsolved hint advancement, server acceptance timing/order, locked solved hints, summary/final ranking and exact ties with 80 seconds per unsolved round. Original gameplay oracles run unchanged where the fixture matches; browser journeys verify controls, private observer perspective, revealed target and canonical answer retry.", "bindings": [ { "file": "tests/acceptance/boundary-counterparts.test.ts", "title": "independent unsolved answer cases", "sourceTest": "AC-45 supplemental independent unsolved answer cases normalize canonical and approved alternatives, reject fuzzy/punctuation changes and lock later scoring", "line": 160, "sourceHash": "0f7428681b0330e9edd52c7cf2b4f2b9768eeab94d14025ceec36234a5e5e0e5", "assertedObservations": [ { "observation": "result.correct", "statements": [ { "line": 178, "expression": "expect(result.correct).toBe(accepted)" }, { "line": 178, "expression": "expect(result.correct)" } ] }, { "observation": "solves.reduce", "statements": [ { "line": 179, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n ).toBe(accepted ? 5 : 0)" }, { "line": 179, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n )" }, { "line": 183, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n ).toBe(accepted ? 5 : 0)" }, { "line": 183, "expression": "expect(\n h.store.state.rounds[0].solves.reduce((sum, s) => sum + s.points, 0),\n )" } ] }, { "observation": "h.answer", "statements": [ { "line": 182, "expression": "expect((await h.answer(\"p1\", input)).ok).toBe(false)" }, { "line": 182, "expression": "expect((await h.answer(\"p1\", input)).ok)" } ] } ], "executions": [ { "title": "AC-45 supplemental independent unsolved answer cases normalize canonical and approved alternatives, reject fuzzy/punctuation changes and lock later scoring", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/match.ts", "src/application/matches.ts", "src/domain/catalog.ts", "src/web/game-panel.tsx" ], "fixtureDiscrepancy": [ "Fixture names and DTO labels may differ; assertions bind the approved behavior rather than copied conforming output." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-47-T01", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "actor": "player", "caseVersion": 4, "catalogEntries": 0, "auditCount": 2 }, "actions": [ { "type": "moderate" }, { "type": "activate-catalog" } ] }, "assertionGroups": [ { "oraclePaths": [ "initial.caseVersion", "initial.catalogEntries", "initial.auditCount", "attempts.0.actor", "attempts.0.command", "attempts.0.result", "attempts.1.actor", "attempts.1.command", "attempts.1.result", "final.caseVersion", "final.catalogEntries", "final.auditCount" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-47-T02", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "caseId": "n1", "countBefore": 9, "moderation": "pending" }, "actions": [ { "type": "add-nomination" }, { "type": "approve-moderation", "payload": { "actor": "m1" } } ] }, "assertionGroups": [ { "oraclePaths": [ "audit.0.action", "audit.0.before", "audit.0.after", "audit.1.action", "audit.1.before", "audit.1.after", "audit.2.action", "audit.2.actor", "playableBeforeCuration" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "activation requires an approved case", "sourceTest": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "line": 28, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "activateCase", "statements": [ { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,2,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,player('p1'),packageData,3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,{...packageData,pairs:[{...pair,hints:['one','','three']}]},3,catalog).ok)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok).toBe(false)" }, { "line": 29, "expression": "expect(activateCase(state,id,mod,packageData,4,catalog).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 29, "expression": "expect(state.publications).toHaveLength(1)" }, { "line": 29, "expression": "expect(state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 29, "expression": "expect(state.cases[0].playable).toBe(true)" }, { "line": 29, "expression": "expect(state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-16/18/47 activation requires an approved case plus complete matching curated records and authorized editor; snapshots stay immutable", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "normal independent moderator approval", "sourceTest": "AC-47-T02 normal independent moderator approval records actor reason threshold eligibility and no activation", "line": 102, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"approved\",\n at: 100,\n details: {\n caseId: id,\n previousStatus: \"pending\",\n status: \"approved\",\n nominationCount: 10,\n reason: \"Complete evidence verified\",\n futureEligible: true,\n playable: false,\n },\n })" }, { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" } ] }, { "observation": "publications", "statements": [ { "line": 176, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 176, "expression": "expect(store.state.publications)" } ] }, { "observation": "playable", "statements": [ { "line": 162, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"approved\",\n at: 100,\n details: {\n caseId: id,\n previousStatus: \"pending\",\n status: \"approved\",\n nominationCount: 10,\n reason: \"Complete evidence verified\",\n futureEligible: true,\n playable: false,\n },\n })" }, { "line": 177, "expression": "expect(store.state.cases[0].playable).toBe(false)" }, { "line": 177, "expression": "expect(store.state.cases[0].playable)" } ] } ], "executions": [ { "title": "AC-47-T02 normal independent moderator approval records actor reason threshold eligibility and no activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" }, { "tier": "supplemental", "id": "AC-47-T03", "originalSource": "tests/ac-contracts/oracle-supplemental-scenarios.ts", "originalRequest": { "setup": { "caseId": "n1", "initial": { "version": 4, "audit": [ "original-rejection" ] } }, "actions": [ { "type": "appeal", "payload": { "actor": "proposer" } }, { "type": "resolve", "payload": { "actor": "senior", "outcome": "uphold" } }, { "type": "resolve", "payload": { "actor": "original-reviewer" } }, { "type": "appeal", "payload": { "actor": "proposer" } }, { "type": "simulate-later-transaction-failure" } ] }, "assertionGroups": [ { "oraclePaths": [ "committed.caseVersion", "committed.audit.0", "committed.audit.1", "committed.audit.2", "committed.publicationCount", "rejectedActions.0.actor", "rejectedActions.0.command", "rejectedActions.0.result", "rejectedActions.1.actor", "rejectedActions.1.command", "rejectedActions.1.result", "laterTransactionFailure.result", "laterTransactionFailure.caseVersion", "laterTransactionFailure.audit.0", "laterTransactionFailure.audit.1", "laterTransactionFailure.audit.2", "laterTransactionFailure.publicationCount" ], "relationship": "Related leaf group: the scoped statements below jointly prove this case; DTO/fixture differences are explicit.", "counterpartMeaning": "Current revealed no-hint solver only; normalized unique profile/candidate and one eligible-round nomination; 9/10 threshold and approval remain unplayable before complete curation. Ten lifetime wins, exactly three distinct pairs and one open suggestion. Independent reasoned moderation; one proposer appeal; distinct senior uphold/overturn; forbidden original reviewer and second appeal. Captured transition audits assert actor, reason, previous/new status, count, eligibility and publication fields. Failed/forbidden actions preserve committed state/audits; activation alone introduces complete immutable future content.", "bindings": [ { "file": "tests/acceptance/lobby-content-counterparts.test.ts", "title": "audit fields follow threshold", "sourceTest": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "line": 525, "sourceHash": "061cb502c54fcee9dc0039424106c1249edfda3940e11f886b1be0608634e4c3", "assertedObservations": [ { "observation": "transition.audits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "store.state", "statements": [ { "line": 610, "expression": "expect(store.state).toEqual(before)" }, { "line": 610, "expression": "expect(store.state)" }, { "line": 655, "expression": "expect(store.state).toEqual(before)" }, { "line": 655, "expression": "expect(store.state)" }, { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" }, { "line": 701, "expression": "expect(store.state).toEqual(accepted)" }, { "line": 701, "expression": "expect(store.state)" } ] }, { "observation": "store.commits", "statements": [ { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: actor,\n action: \"nominate\",\n at: 1,\n details: {\n nominationCount: i + 1,\n status: i < 9 ? \"collecting\" : \"pending\",\n futureEligible: false,\n playable: false,\n },\n })" }, { "line": 576, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 611, "expression": "expect(store.commits).toHaveLength(count)" }, { "line": 611, "expression": "expect(store.commits)" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m1\",\n action: \"rejected\",\n details: {\n previousStatus: \"pending\",\n status: \"rejected\",\n reason: \"Needs evidence\",\n nominationCount: 10,\n futureEligible: false,\n },\n })" }, { "line": 624, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"p0\",\n action: \"appeal\",\n details: {\n previousStatus: \"rejected\",\n status: \"appealed\",\n reason: \"New evidence\",\n },\n })" }, { "line": 637, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0]).toMatchObject({\n actorId: \"m2\",\n action: outcome === \"uphold\" ? \"upheld\" : \"overturned\",\n details: {\n previousStatus: \"appealed\",\n status: outcome === \"uphold\" ? \"rejected\" : \"approved\",\n reason: \"Evidence verified\",\n futureEligible: outcome === \"overturn\",\n playable: false,\n },\n })" }, { "line": 668, "expression": "expect(store.commits.at(-1)?.transition.audits[0])" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits)).toEqual(\n acceptedAudits,\n )" }, { "line": 702, "expression": "expect(store.commits.flatMap((c) => c.transition.audits))" } ] }, { "observation": "publications", "statements": [ { "line": 679, "expression": "expect(store.state.publications).toEqual([])" }, { "line": 679, "expression": "expect(store.state.publications)" } ] } ], "executions": [ { "title": "AC-18/47 primary and supplemental audit fields follow threshold, review, one appeal and independent resolution without forbidden mutations or publication", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] }, { "file": "tests/application/content.test.ts", "title": "allows one proposer appeal", "sourceTest": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "line": 22, "sourceHash": "b5efe91d5eec3305ae168b073cb98822836ac926d0034a50d15aef1e6f51f448", "assertedObservations": [ { "observation": "resolveAppeal", "statements": [ { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,{profileId:'m1',roles:['senior-moderator']},outcome,'Reason',4).ok)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok).toBe(false)" }, { "line": 23, "expression": "expect(resolveAppeal(state,id,mod,outcome,'Reason',4).ok)" } ] }, { "observation": "appealCase", "statements": [ { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p9'),'Context',3).ok)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok).toBe(false)" }, { "line": 23, "expression": "expect(appealCase(state,id,player('p0'),'Again',5).ok)" } ] }, { "observation": "publications", "statements": [ { "line": 23, "expression": "expect(state.publications).toHaveLength(0)" }, { "line": 23, "expression": "expect(state.publications)" } ] }, { "observation": "status", "statements": [ { "line": 23, "expression": "expect(state.cases[0].status).toBe(outcome==='uphold'?'rejected':'approved')" }, { "line": 23, "expression": "expect(state.cases[0].status)" } ] } ], "executions": [ { "title": "AC-18/47 allows one proposer appeal and a distinct senior reasoned uphold/overturn without activation", "status": "passed", "artifact": "docs/evidence/phase6/unit.json" } ] } ] } ], "productionEntryPoints": [ "src/domain/content.ts", "src/application/content.ts", "src/web/content-panel.tsx", "src/web/social-panel.tsx" ], "fixtureDiscrepancy": [ "Production stores independent nomination votes and a separate content aggregate; historical observer/state/error DTO spellings are mapped by their policy meaning." ], "evidenceTier": "Actual domain/application decisions with port fakes or local process contract; no database durability claim", "rawObservations": "Specific production observations are evaluated live by the scoped asserted statements; transaction histories and browser metric JSON are retained in docs/evidence/phase6 where produced.", "command": "npm test; WORDPAIRS_BROWSER_PATH=/usr/bin/chromium npm exec playwright -- test --config playwright.phase6.config.ts; generate mapping with WORDPAIRS_ACCEPTANCE_MANIFEST and matching run/source-hash files" } ] }