You are michel in collaborative LAB-005 Round 3. Saitz selected Delores; do not propose a redesign. Review only the persistent-state correction. Return ONLY one compact JSON object with exactly these keys: participant, role, invariants, patch_steps, hook_requirements, edge_cases, preserve, risks. Values other than participant/role must be concise arrays of strings. No markdown. Do not use tools or modify files. PACKET: {"schema_version":"1.0","experiment_id":"LAB-005","round_id":"LAB-005-R3","assignment_id":"LAB-005-R3-michel-recommendation","participant_id":"michel","kind":"review","objective":"Design deterministic persistence proof and flag accessibility regressions; review the compact excerpt, not full source.","allowed_sources":["experiments/LAB-005/ROUND3-SELECTION.md","experiments/LAB-005/ROUND3-COLLABORATION.md","round-03-collaboration/shared/delores-state-excerpt.js","Declared selected-source hash and defect locations"],"prohibited_actions":["No competing redesign or replacement concept","No model/provider/profile/machine fallback or substitution","No customer/production data, network access, credentials, or file writes","Do not return HTML; return only the requested compact JSON recommendation","Do not reset persistent board/game totals/sequence during question transitions"],"required_sections":["JSON object keys: participant, role, invariants, patch_steps, hook_requirements, edge_cases, preserve, risks","Concise actionable recommendations grounded in the selected Delores foundation","Explicit board/score/lines/piece-sequence persistence and line-clear bonus guidance"],"success_criteria":["Recommendation preserves Delores visual direction","Recommendation addresses only persistent cross-question game state","Recommendation is usable by Delores for one final complete HTML synthesis","No fallback/substitution"],"packet_version":"v4","created_at":"2026-07-11T23:48:45.426687+00:00"} CONTRACT: # LAB-005 Round 3 — Delores Winner Collaboration Contract ## Human decision and immutable base Saitz selected Delores's `round-02-correction/builds/delores.html` (`eb01f19a3892e1039422fe4d760479282650cfafa62728cf0e73676c89cd2883`). Round 3 is one collaborative correction, not a new competition. Preserve the selected visual direction, information architecture, controls, synthetic questions, and literal falling-block loop. ## Only product correction The board is one continuous game for the whole review session. A question grants one initial piece. A line-clearing lock grants an immediate bonus piece on that same board. A no-line lock pauses play and advances to the next question. Submitting that review resumes the unchanged board and grants the next piece. Never reset locked cells, score, cleared-line count, piece count/sequence, or game-over state during question transitions. The final summary appears only after every reviewed question's earned turn ends, or game over. The selected source currently violates this contract because both `showReview()` and `startGameTurn()` assign `state.board = createBoard()`. ## Roles and outputs All participants review the same correction. Each recommendation must be concise JSON matching its packet—no HTML and no unrelated redesign. - **Delores:** visual/interaction guardian and implementation proposal. Identify the smallest patch that preserves the winner's design. - **Foster:** gameplay/state-machine reviewer. Specify invariants, transitions, piece-sequence persistence, line bonus, and summary timing. - **Michel:** deterministic test-hook/accessibility reviewer. Work from the compact source excerpt; full source is not required. - **Rafael:** product-loop/edge-case reviewer. Check final-question, bonus chains, game-over, timer, and restart boundaries. After all four recommendations are accepted, Delores is the sole synthesizer. She receives the hash-verified complete selected HTML through a job-local prompt file plus all peer recommendations and returns one complete self-contained HTML document. No fallback or profile/provider/model substitution is permitted. ## Required deterministic API `window.__reviewArcadeTest` must expose serializable state and deterministic methods that can perform the complete proof without sleeps: 1. seed a recognizable locked-cell pattern and known score/line/piece-sequence state; 2. end a no-line turn; 3. advance to the next review and submit it; 4. resume gameplay and prove the original pattern, score, lines, and sequence position persisted; 5. prepare and execute a line clear on that persistent board; 6. prove line/score increment and immediate bonus-piece activation. Recommended names: `getState`, `seedPersistentState`, `finishNoLineTurn`, `submitCurrentReview`, `preparePersistentLineClear`, and `hardDrop`. Equivalent documented names are acceptable only if the browser verifier can invoke them deterministically. ## Acceptance gates - Selected source hash remains the declared foundation; prior artifacts are never overwritten. - Final output is complete offline HTML with no external dependencies or CLI contamination. - Every inline script passes `node --check`. - Static inspection finds no `state.board = createBoard()` in question-transition functions (`showReview`, `startGameTurn`, or equivalents); reset remains allowed only for explicit session restart and deterministic fixture setup. - Browser proof asserts board/score/lines/piece sequence persist across a no-line question transition. - Browser proof asserts a line clear increments totals and starts an immediate bonus piece on the same board. - No automatic acceptance if Playwright/Chromium or required hooks are unavailable. COMPACT HASHED SOURCE EXCERPT: /* SOURCE LINES 811-913 */ var state = { phase: "review", questionIndex: 0, selectedDisposition: "", board: createBoard(), current: null, reviews: [], score: 0, lines: 0, piecesPlayed: 0, bonusPieces: 0, bonusActive: false, gameOver: false, transitionPending: false, lastClearCount: 0, testMode: new URLSearchParams(window.location.search).get("test") === "1" }; var gravityTimer = null; var transitionTimer = null; function createBoard() { return Array.from({ length: ROWS }, function () { return Array(COLS).fill(null); }); } function clone(value) { return JSON.parse(JSON.stringify(value)); } function currentMatrix(piece) { return pieces[piece.name].rotations[piece.rotation]; } function collision(piece, dx, dy, rotation) { var matrix = pieces[piece.name].rotations[ rotation === undefined ? piece.rotation : rotation ]; for (var y = 0; y < matrix.length; y += 1) { for (var x = 0; x < matrix[y].length; x += 1) { if (!matrix[y][x]) continue; var boardX = piece.x + x + (dx || 0); var boardY = piece.y + y + (dy || 0); if (boardX < 0 || boardX >= COLS || boardY >= ROWS) return true; if (boardY >= 0 && state.board[boardY][boardX]) return true; } } return false; } function makePiece(name) { var selectedName = name || pieceNames[Math.floor(Math.random() * pieceNames.length)]; var matrix = pieces[selectedName].rotations[0]; return { name: selectedName, rotation: 0, x: Math.floor((COLS - matrix[0].length) / 2), y: 0 }; } function startGravity() { stopGravity(); gravityTimer = window.setInterval(function () { if (state.phase === "game" && state.current && !state.transitionPending && !state.gameOver) { stepDown(false); } }, GRAVITY_MS); } function stopGravity() { if (gravityTimer !== null) { window.clearInterval(gravityTimer); gravityTimer = null; } } function spawnPiece(isBonus, forcedName) { state.bonusActive = Boolean(isBonus); state.current = makePiece(forcedName); state.transitionPending = false; if (collision(state.current, 0, 0)) { handleGameOver(); return false; } updateGameStatus( isBonus ? "Bonus piece active" : "Piece active", isBonus ? "The last lock cleared a line, so this piece starts immediately." : "Move, rotate, or drop the falling piece." ); updateTurnBadge(); draw(); startGravity(); return true; } /* SOURCE LINES 981-1155 */ function lockPiece() { if (!state.current || state.transitionPending) return; stopGravity(); var matrix = currentMatrix(state.current); var lockedAboveTop = false; for (var y = 0; y < matrix.length; y += 1) { for (var x = 0; x < matrix[y].length; x += 1) { if (!matrix[y][x]) continue; var boardX = state.current.x + x; var boardY = state.current.y + y; if (boardY < 0) { lockedAboveTop = true; } else { state.board[boardY][boardX] = pieces[state.current.name].color; } } } state.current = null; state.piecesPlayed += 1; if (lockedAboveTop) { updateStats(); draw(); handleGameOver(); return; } var cleared = clearCompletedRows(); state.lastClearCount = cleared; if (cleared > 0) { state.lines += cleared; state.score += [0, 100, 300, 500, 800][Math.min(cleared, 4)]; state.bonusPieces += 1; updateStats(); draw(); spawnPiece(true); return; } state.bonusActive = false; state.transitionPending = true; updateStats(); draw(); updateGameStatus("Turn complete", "No line cleared. Returning to the next unanswered review question."); transitionTimer = window.setTimeout(function () { state.transitionPending = false; finishReviewedTurn(); }, state.testMode ? 0 : 650); } function clearCompletedRows() { var cleared = 0; for (var y = ROWS - 1; y >= 0; y -= 1) { var complete = state.board[y].every(function (cell) { return cell !== null; }); if (complete) { state.board.splice(y, 1); state.board.unshift(Array(COLS).fill(null)); cleared += 1; y += 1; } } return cleared; } function finishReviewedTurn() { state.questionIndex += 1; if (state.questionIndex >= questions.length) { showSummary(false); } else { showReview(); } } function handleGameOver() { stopGravity(); state.current = null; state.gameOver = true; state.transitionPending = false; updateGameStatus("Game over", "The stack blocked a new piece. The session will close with an explicit game-over summary."); draw(); transitionTimer = window.setTimeout(function () { showSummary(true); }, state.testMode ? 0 : 850); } function showReview() { stopGravity(); state.phase = "review"; state.current = null; state.bonusActive = false; state.selectedDisposition = ""; state.board = createBoard(); document.getElementById("reviewScreen").hidden = false; document.getElementById("gameScreen").hidden = true; document.getElementById("summaryScreen").hidden = true; var question = questions[state.questionIndex]; document.getElementById("questionNumber").textContent = "Question " + (state.questionIndex + 1); document.getElementById("reviewHeading").textContent = question.question; document.getElementById("draftAnswer").textContent = question.draft; document.getElementById("evidenceState").textContent = question.evidence; document.getElementById("reviewNote").value = ""; document.getElementById("reviewError").textContent = ""; document.getElementById("noteRequirement").textContent = "(optional for Approve)"; document.getElementById("submitReview").disabled = true; Array.from(document.querySelectorAll(".disposition")).forEach(function (button) { button.classList.remove("selected"); button.setAttribute("aria-pressed", "false"); }); updateProgress(); } function submitReview(event) { event.preventDefault(); var disposition = state.selectedDisposition; var note = document.getElementById("reviewNote").value.trim(); var error = document.getElementById("reviewError"); if (!disposition) { error.textContent = "Choose a disposition."; return; } if (disposition !== "Approve" && !note) { error.textContent = "Add a note for Fix, Evidence Gap / Blank, or Escalate."; return; } error.textContent = ""; state.reviews.push({ questionIndex: state.questionIndex, question: questions[state.questionIndex].question, disposition: disposition, note: note }); startGameTurn(); } function startGameTurn() { state.phase = "game"; state.board = createBoard(); state.current = null; state.bonusActive = false; state.lastClearCount = 0; state.transitionPending = false; document.getElementById("reviewScreen").hidden = true; document.getElementById("gameScreen").hidden = false; document.getElementById("summaryScreen").hidden = true; updateProgress(); updateStats(); draw(); spawnPiece(false); } /* SOURCE LINES 1397-1547 */ function serializableState() { return { phase: state.phase, questionIndex: state.questionIndex, questionCount: questions.length, selectedDisposition: state.selectedDisposition, board: clone(state.board), current: state.current ? clone(state.current) : null, reviews: clone(state.reviews), score: state.score, lines: state.lines, piecesPlayed: state.piecesPlayed, bonusPieces: state.bonusPieces, bonusActive: state.bonusActive, gameOver: state.gameOver, transitionPending: state.transitionPending, lastClearCount: state.lastClearCount, gravityActive: gravityTimer !== null, testMode: state.testMode }; } function ensureTestGame() { if (transitionTimer !== null) { window.clearTimeout(transitionTimer); transitionTimer = null; } stopGravity(); state.phase = "game"; state.gameOver = false; state.transitionPending = false; state.current = null; document.getElementById("reviewScreen").hidden = true; document.getElementById("gameScreen").hidden = false; document.getElementById("summaryScreen").hidden = true; } function prepareNearCompleteRow() { ensureTestGame(); state.board = createBoard(); for (var x = 0; x < COLS; x += 1) { state.board[ROWS - 1][x] = (x >= 3 && x <= 6) ? null : "#b8c6b1"; } state.lastClearCount = 0; state.bonusActive = false; draw(); updateStats(); updateGameStatus("Test row prepared", "The bottom row is open in columns 3 through 6."); return serializableState(); } function forceClearingPiece() { ensureTestGame(); state.current = { name: "I", rotation: 0, x: 3, y: 0 }; state.bonusActive = false; draw(); updateTurnBadge(); updateGameStatus("Clearing piece ready", "Hard drop the horizontal I piece to complete the prepared row."); return serializableState(); } function forceGameOver() { ensureTestGame(); state.board = createBoard(); for (var y = 0; y < 4; y += 1) { for (var x = 0; x < COLS; x += 1) { state.board[y][x] = "#bd7676"; } } state.current = null; draw(); spawnPiece(false, "O"); return serializableState(); } window.__reviewArcadeTest = { getState: serializableState, prepareNearCompleteRow: prepareNearCompleteRow, forceClearingPiece: forceClearingPiece, hardDrop: function () { hardDrop(); return serializableState(); }, moveLeft: function () { moveHorizontal(-1); return serializableState(); }, moveRight: function () { moveHorizontal(1); return serializableState(); }, rotate: function () { rotatePiece(); return serializableState(); }, softDrop: function () { stepDown(true); return serializableState(); }, inspectBonusPieceActivation: function () { return { bonusActive: state.bonusActive, bonusPieces: state.bonusPieces, lastClearCount: state.lastClearCount, currentPiece: state.current ? clone(state.current) : null, phase: state.phase }; }, forceGameOver: forceGameOver, startQuestionGame: function () { if (state.phase === "review") { state.selectedDisposition = "Fix"; document.getElementById("reviewNote").value = "Synthetic deterministic test correction."; state.reviews.push({ questionIndex: state.questionIndex, question: questions[state.questionIndex].question, disposition: "Fix", note: "Synthetic deterministic test correction." }); } startGameTurn(); return serializableState(); }, finishNoLineTurn: function () { ensureTestGame(); state.board = createBoard(); state.current = { name: "O", rotation: 0, x: 4, y: ROWS - 2 }; lockPiece(); return serializableState(); }, restart: function () { restart(); return serializableState(); } };