fix(rooms): mutações de sala param de responder ok:true sem confirmar a escrita (#56) - #96
Merged
Merged
Conversation
… a escrita (#56) As actions `settings`, `start`, `reset` e `claim-leader` faziam `.update()` sem desestruturar o `error` — e o supabase-js DEVOLVE `{ error }` em vez de lançar. Constraint violada (a 0004 pendente torna isso provável), RLS ou timeout viravam "deu certo" na tela do líder, com a sala parada no lobby e nenhum log. - `roomUpdateOutcome` em `src/lib/room.ts`: decisão pura (erro ⇒ 500, zero linhas confirmadas ⇒ 409 neutro, ≥1 linha ⇒ ok), coberta por 5 casos no Vitest. - `applyRoomUpdate` em `route.ts`: adaptador fino que encadeia `.select("code")`, loga o detalhe no servidor e devolve `NextResponse` de erro ou `null`. - `finish` passa a reportar o `error`, PRESERVANDO `flipped.length === 0` como "outro cliente já finalizou" ⇒ segue `ok: true` sem repersistir. - GET deixa de devolver `error.message` do PostgREST ao cliente (virava toast). Fora do escopo, declarado na PR: `persistMatch` (colide com a #55) e `src/app/api/rooms/route.ts`.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 26, 2026
Owner
Author
✅ PR Doctor — APROVADA (rooms API / honestidade de escrita)Diff lido inteiro. Decisão pura
CI + Vercel verdes. |
caioross
added a commit
that referenced
this pull request
Jul 26, 2026
Uniao com origin/main (que trouxe #60/#79/#96/#89). Resolucao: - room.ts: mantidos os builders (buildMatchRow/buildScoreRows/MatchInsert/ ScoreInsert) E as funcoes de kick/vote/outcome que entraram na main. - route.ts: imports unificados; persistMatch segue como wrapper fino dos builders. - room.test.ts: imports unificados; adicionado kicked_ids:[] ao FULL_ROOM (campo virou obrigatorio em RoomRow via #79). Gate: typecheck OK, build OK, test 103 OK, validate-persistence 72/0.
caioross
added a commit
that referenced
this pull request
Jul 26, 2026
Uniao com origin/main (#55/#60/#79/#89/#96). Resolucao (tudo aditivo): - useRoom.ts: mantidos isSpectatorJoin + isSpectator E as importacoes/logica de votacao (#89, tallyVotes/pickVoteWinner/activeVotes/voteTally/myVote). - RoomView.tsx: mantido o branch isSpectator?SpectatorView:Race E os props isLeader/onKick do Race (#79), alem de voteTally/myVote no destructure. - room.test.ts: mantidos os blocos de isSpectatorJoin (#64) e votacao/outcome. Gate: typecheck OK, test 109 OK, build OK, validate-persistence 72/0.
This was referenced Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexto
Em
src/app/api/rooms/[code]/route.ts, quatro das cinco actions faziamawait sb.from("rooms").update(...)sem desestruturar oerrore retornavamok: trueincondicionalmente. O supabase-js devolve{ error }em falha doPostgREST em vez de lançar — então constraint violada, RLS ou timeout viravam
"deu certo" na tela do líder, com a sala parada no lobby e nenhum log.
O cenário concreto da issue: a migração
0004_settings_allowlist.sqlaguardaaplicação pelo dono e adiciona constraints
NOT VALID(não validam linhaslegadas, validam todo UPDATE novo). Sala antiga + mudança de linguagem =
UPDATE que falha e UI que confirma o que nunca aconteceu.
O que mudou e por quê
src/lib/room.tsroomUpdateOutcome({ error, rows })— decisão pura:error⇒ 500 + copy neutra · zero linhas confirmadas ⇒ 409 neutro · ≥1 linha ⇒ ok.src/lib/room.test.tsroute.tsapplyRoomUpdate(sb, code, action, patch)— adaptador fino: encadeia.select("code"), loga o detalhe no servidor, devolveNextResponsede erro ounull. Aplicado emsettings,start,reseteclaim-leader.route.ts(finish)error, preservandoflipped.length === 0= "outro cliente já finalizou" ⇒ segueok: truesem repersistir (AC 3 não regride).route.ts(GET)error.messagedo PostgREST ao cliente —useRoom.ts:84exibe essa string em toast, ou seja, era schema na tela do jogador.Duas decisões que valem destacar:
getServerSupabasecai para achave anon quando não há service_role; com RLS, "0 linhas" pode ser SELECT
negado, não sala apagada. A resposta não afirma o que o servidor não sabe.
kicked_idsnoresetcontinua best-effort (inerte enquanto a0005 não for aplicada); só o reset em si passa a exigir confirmação.
Zero mudança de cliente:
postActionjá fazif (!json?.ok) fail(...)→toast. As strings novas são copy de produto, no registro das existentes.
Escopo declarado (fora)
persistMatch(route.ts:~166) engoleerrordosinsert— colisão frontalcom a PR refactor(persistencia): extrai buildMatchRow/buildScoreRows de persistMatch e cobre com Vitest (#50) #55, que está refatorando exatamente essa função. Segue a
recomendação do Conselho: follow-up depois que a refactor(persistencia): extrai buildMatchRow/buildScoreRows de persistMatch e cobre com Vitest (#50) #55 mergear.
src/app/api/rooms/route.ts(:29,:88) tem o mesmo vazamento deerror.message— outra issue.claim-leader(Segurança: claim-leader permite sequestro de liderança de qualquer sala (sem autorização) #6 / PR fix(security): exige sala estagnada para claim-leader (anti-sequestro) #12) não muda aqui: a action só passaa reportar falha de escrita. Esse hunk é o último do diff, descartável se a fix(security): exige sala estagnada para claim-leader (anti-sequestro) #12
mergear antes.
Gate (rodado no worktree, resultado real)
pnpm install --frozen-lockfilepnpm typecheckpnpm buildpnpm testnode scripts/validate-persistence.mjsvalidate-metrics.mjspnpm lintRiscos
É o objetivo da issue, mas é mudança observável em produção.
route.tsestá aberto em outras PRs (fix(anti-cheat): bloqueia drop na textarea da corrida — arrastar o snippet-alvo não digita mais (#61) #94, fix(anti-cheat): rejeita finish temporalmente impossível e 409 honesto (#34) #36, fix(security): exige sala estagnada para claim-leader (anti-sequestro) #12).O diff é cirúrgico e o rebase é assumido.
.select("code")custa uma ida ao banco por clique de líder, no lobby.Segui o plano do Parecer do Conselho de 2026-07-24 na íntegra (função pura em
room.ts+ adaptador na rota, 409 neutro,finishpreservado,persistMatchfora) — a única adaptação foi a renumeração das linhas, que mudaram com o merge
da action
kick.Solicito quórum (HANDBOOK §7)
Closes #56