## MG-6 — Sincronização e lembretes no Cloud Scheduler

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

**Como** participante, **quero** que o placar chegue sozinho e o lembrete de prazo
me alcance, **para** não perder rodada por não ter aberto o app.

**Prioridade:** Must — é a task de infraestrutura que ficou aberta desde o começo,
e é a que mais facilmente falha em silêncio.

**Critérios de aceite:**

- Dado que a fonte oficial mudou horário ou placar, quando a sincronização roda,
  então a mudança é aplicada e os gatilhos de `PP-6` e `PR-6` disparam.
- Dado que a fonte está indisponível, então **nada** do que já está armazenado é
  sobrescrito, e a falha é registrada.
- Dado que duas sincronizações concorrem, então só uma executa — a outra é
  ignorada, sem duplicar correção nem aviso.
- Dado que o job ficou horas parado, então a primeira execução após a volta aplica
  **todas** as mudanças acumuladas.
- Dado que o prazo de um jogo está próximo e não palpitei, então recebo lembrete —
  e **não** recebo se já palpitei, nem duas vezes pelo mesmo jogo.
- Dado que saí ou fui removido do bolão, então paro de receber lembrete dele.
- Dado que o processo não roda, então isso **aparece** — job dormindo em silêncio é
  o modo de falha mais caro deste produto.

**Tasks:**

_Backend_
- [ ] `BE-1` Function agendada de sincronização com a `football-data.org`, reusando o adaptador atual (depende de MG-2 BE-1)
- [ ] `BE-2` Lock em documento com transação, no lugar da trava em processo (depende de BE-1)
- [ ] `BE-3` Function agendada de lembrete de prazo, idempotente por jogo e participante (depende de MG-2 BE-1)
- [ ] `BE-4` Push por FCM no lembrete, com o aviso gravado mesmo sem push (depende de BE-3)
- [ ] `BE-5` Tolerância a falha: erro da fonte não sobrescreve dado bom (depende de BE-1)
- [ ] `BE-6` Respeitar o limite de 10 req/min da fonte, mantendo cache e cota (depende de BE-1)

_Mobile_
- [ ] `FE-1` Pedir permissão de notificação sem travar o app quando negada (depende de BE-4)
- [ ] `FE-2` Toque no lembrete abre a tela de palpite do bolão certo, com o app fechado (depende de BE-4)

_DevOps_
- [ ] `OPS-1` Cloud Scheduler configurado: sincronização horária, lembrete a cada 10 min
- [ ] `OPS-2` Alarme de job que parou de rodar — silêncio não pode ser confundido com sucesso
- [ ] `OPS-3` Credenciais do FCM e da fonte fora do repositório

_QA_
- [ ] `QA-1` Escrever o plano de casos de teste a partir desta story
- [ ] `QA-2` Executar reaproveitando `PP-6`, `PP-8` e `PP-10`, incluindo concorrência e job parado

**Riscos/Dependências:**

- Depende de `MG-2`. **Exige Blaze**: Scheduler e Functions agendadas não existem
  no plano gratuito.
- **Fecha `PP-10 OPS-1`** (hospedagem que não hiberne) e as 4 tasks de `PP-8`
  presas ao FCM.
- A trava de concorrência de 07/08 é **em processo** e deixa de servir: cada
  invocação é um processo novo. Sem `BE-2`, o defeito que já foi corrigido volta.
- iOS continua sem push: o APNs exige Apple Developer Program pago.
