## MG-3 — Sigilo do palpite e prazo aplicados nas security rules

**PRD:** `docs/prd/migracao-firebase.md`

**Como** participante, **quero** que meu palpite continue secreto até o jogo
terminar e o prazo continue sendo do servidor, **para** a competição não virar
disputa de quem viu o palpite de quem.

**Prioridade:** Must — são as duas regras que, quebradas, corrompem o bolão.

**Critérios de aceite:**

- Dado um jogo que não terminou, quando qualquer pessoa — **inclusive o
  administrador** — tenta ler o palpite de outro, então a leitura é recusada.
- Dado que o jogo terminou, então os palpites de todos ficam legíveis para os
  participantes.
- Dado que o jogo **começou mas não terminou**, então o palpite continua secreto:
  libera no fim, não no apito.
- Dado que o relógio do meu aparelho está adiantado, quando tento palpitar depois
  do apito, então a escrita é recusada pelo **`request.time`** do servidor.
- Dado que altero o palpite antes do apito, então a alteração é aceita e a versão
  anterior fica registrada.
- Dado que a Function que libera o sigilo falhar, então o palpite fica secreto **a
  mais** — nunca exposto antes do fim do jogo.

**Tasks:**

_Backend_
- [x] `BE-1` Flag `jogoTerminado` no documento de palpite, escrita só por Function (depende de MG-2 BE-1)
- [x] `BE-2` Rule de leitura de palpite: dono sempre, terceiros só com o jogo terminado (depende de BE-1)
- [x] `BE-3` Rule de escrita de palpite comparando `request.time` com o `kickoffAt` do jogo (depende de MG-2 BE-1)
- [x] `BE-4` Function no gatilho de status do jogo, virando status e flag **na mesma transação** (depende de BE-1)
- [x] `BE-5` Revisão do palpite registrada na alteração de valor, e só nela (depende de BE-3)

_QA_
- [x] `QA-1` Escrever o plano de casos de teste a partir desta story
- [x] `QA-2` Executar reaproveitando os IDs de `PP-7` (sigilo), `PP-5` (apito) e `PP-2` (revisão), incluindo a falha da Function

**Riscos/Dependências:**

- Depende de `MG-1` e `MG-2`.
- 🔴 **O mecanismo do sigilo muda.** Hoje é filtro em consulta; passa a ser flag
  desnormalizada. O risco sai de "a consulta vaza" e vai para "a Function não
  rodou" — e por isso `QA-2` tem de testar a Function **não** rodando.
- Security rule não filtra resultado de query: listagem passa inteira ou falha
  inteira. É o que obriga a desnormalização.

- **07/08 — feita e verificada.** 34 casos (26 de security rule + 8 de integração
  com a Function), suíte total de 66. Plano em `docs/testes/MG-3-plano.md`, execução
  em `docs/testes/MG-3-execucao.md`.
- **Descoberta que muda o app: rules não são filtros.** Numa consulta, a rule é
  avaliada **contra a consulta** — o `where` precisa provar o acesso. Filtrar
  palpites só por jogo deixa `userId` e `matchFinished` indefinidos para o avaliador
  e é recusado, mesmo que todos os documentos fossem legíveis. A tela de "palpites de
  todos" precisa consultar `matchId == X` **e** `matchFinished == true`. Sem o caso
  `MG-3-07`, isso cairia em `MG-7` como "o Firestore não deixa".
- **O caso que justifica o desenho é `MG-3-30`:** com a Function não rodando, o
  palpite fica secreto **a mais** — nunca exposto antes. O inverso não tem como
  acontecer, porque só a Function escreve o campo e só no fim com placar.
- **Defeito no teste, não no produto:** os dois casos negativos da Function falharam
  por timeout do Jest (esperar 5 s com limite de 5 s). Um caso negativo com orçamento
  de tempo errado **parece** defeito de regra, e o reflexo de mexer na regra é forte.
- ⚠️ O índice de grupo em `palpites.matchId` está declarado e **não verificado**: o
  emulador não exige índice, e a Function de sigilo depende dele. Verificação
  obrigatória em `MG-8`.
