## PP-4 — Rascunho offline com envio automático

**PRD:** `docs/prd/palpite-e-prazo.md`

**Como** participante casual, **quero** que o palpite que eu digitei sem
sinal seja guardado e enviado sozinho quando a internet voltar, **para** não
perder o palpite feito no ônibus ou no estádio.

**Prioridade:** Must — perder palpite digitado por falha de rede foi
apontado como motivo direto de sair do app.

**Critérios de aceite:**

- Dado que estou sem conexão, quando digito um palpite, então ele é guardado
  no device e eu vejo que ainda não foi enviado.
- Dado que tenho palpite pendente de envio, quando a conexão volta, então ele
  é enviado sozinho e o estado muda para enviado.
- Dado que tenho palpite pendente e fecho o app, quando abro de novo, então o
  palpite pendente ainda está lá.
- Dado que o prazo fechou antes de a conexão voltar, quando o envio é
  tentado, então o servidor recusa e a tela explica que o prazo fechou com o
  palpite não registrado — **sem apagar silenciosamente** o que eu digitei.
- Dado que o envio automático falha por erro de servidor, então o palpite
  permanece pendente e é tentado novamente.

**Tasks:**

_Backend_
- [x] `BE-1` Tornar o registro de palpite idempotente, para reenvio não duplicar (depende de PP-1 BE-2)
- [x] `BE-2` Resposta de recusa por prazo vencido distinguível de erro de servidor (depende de PP-1 BE-2)

_Mobile_
- [x] `FE-1` Persistir rascunho de palpite localmente, sobrevivendo ao fechamento do app
- [x] `FE-2` Indicador de "ainda não enviado" por jogo
- [x] `FE-3` Fila de envio com retry ao recuperar conexão (depende de BE-1)
- [x] `FE-4` Tratar recusa por prazo vencido preservando o que foi digitado (depende de BE-2)

_QA_
- [x] `QA-1` Escrever o plano de casos de teste a partir desta story
- [x] `QA-2` Executar em modo avião, com conexão intermitente, e com prazo fechando durante a fila

**Riscos/Dependências:**

- Depende de PP-1.
- Reenvio sem idempotência pode gerar palpite duplicado — é o risco técnico
  central desta story.
- Rascunho local **não pode** guardar palpite de outro participante (regra de
  sigilo, PP-7).

- **06/08, verificado em device com Wi-Fi e dados desligados:** digitado offline
  fica no aparelho, aparece como "não enviado" por jogo e numa faixa fixa, e a
  fila envia sozinha ao abrir o bolão com rede — sem o participante tocar em
  nada. O banco ficou com **1 revisão** para o palpite, o que prova a
  idempotência de `BE-1` no fluxo real.
- `BE-1` não estava pronto: o `upsert` já era idempotente no palpite, mas **toda**
  chamada gravava uma revisão nova. A fila de reenvio inflaria justamente o
  histórico que existe para resolver disputa de prazo. Agora só mudança de valor
  gera revisão.
- **Limite conhecido:** offline, o participante entra no app (a sessão sobrevive)
  mas **não consegue abrir o bolão**, porque a lista vem da rede. O rascunho não
  se perde e é enviado depois; ver e editar offline depende do cache de
  `CV-6 FE-4`/`CC-6 FE-2`, que seguem abertos.

- **06/08:** plano de casos em `docs/testes/PP-4-plano.md`. Escrito a partir da
  story, não do código — caso escrito depois de olhar a implementação valida o
  que foi feito, não o que foi pedido.

- **06/08 — execução: ✅ 12 de 12** (`docs/testes/PP-4-execucao.md`).
- Três lacunas eram sobre o **armazenamento**, o que ninguém lembra de testar
  porque "é só um json no disco": chave por bolão **e** rodada (`PP-4-10`),
  rascunho corrompido descartado em vez de derrubar a tela (`PP-4-09`), e lista
  vazia **removendo** a chave em vez de gravar `[]` para sempre (`PP-4-11`).

- **06/08 — limite fechado.** A nota anterior dizia que offline o participante
  entrava no app mas **não conseguia abrir o bolão**, porque a lista vinha da
  rede. A lista agora é guardada no aparelho: sem conexão, o app mostra a lista
  com a idade do dado e deixa abrir o bolão para ver o palpite guardado.
