# Linhas de crontab a acrescentar em produção

Preparadas em 28/08/2026. **Nada foi aplicado** — produção está congelada por
decisão de 23/08, e o crontab vive lá.

*Em 11/09/2026 o congelamento ganhou destino:* nada é aplicado em produção até a
**migração final**, depois de todas as validações. Estas linhas entram lá, junto
com os passos 3 a 7 da remediação — ver o aviso no topo de
`docs/debitos-tecnicos.md`.

Este arquivo existe separado da tabela em `f1-6-agendamento.md` de propósito:
**aquela tabela descreve o que roda hoje**, e é dela que
`scripts/conferir-agendamento.php` tira a verdade. Escrever lá o que ainda não
foi aplicado faria a conferência ficar verde sobre produção inalterada — o tipo
de mentira que este projeto já pagou caro para aprender a evitar.

Quando aplicar, **mova a linha para a tabela** e a conferência acompanha.

---

## As linhas

```cron
30 1 * * * curl -m 300 -fsS "https://franquiabv.com.br/api/cronjobs/conveniosAtualizarVencimento" -H "Authorization: Bearer SEU_TOKEN" > /dev/null || logger -t rotinas "falhou (curl $?): /api/cronjobs/conveniosAtualizarVencimento"
35 * * * * curl -m 300 -fsS "https://franquiabv.com.br/api/cronjobs/pushAppLimpar" -H "Authorization: Bearer SEU_TOKEN" > /dev/null || logger -t rotinas "falhou (curl $?): /api/cronjobs/pushAppLimpar"
40 2 * * * curl -m 300 -fsS "https://franquiabv.com.br/api/cronjobs/agendasExclusoesLimpar" -H "Authorization: Bearer SEU_TOKEN" > /dev/null || logger -t rotinas "falhou (curl $?): /api/cronjobs/agendasExclusoesLimpar"
```

**A forma das linhas mudou em 06/10/2026**, com a decisão do dono sobre o
crontab de produção (D331; lote de perguntas, pergunta 5, a opção (b)): o token
vai no cabeçalho, e não na URL, e o `-f` faz o `curl` falhar com 401 ou 500.
**E em 07/11/2026** (a fatia 113; a resposta 128 (b), *"Sem e-mail: o aviso vai
ao log do sistema"*, e a 194 (a), o domínio raiz): no lugar do `MAILTO`, o `||
logger -t rotinas "falhou (curl $?): <a rota>"` de cada linha leva o código de
saída do `curl` e a rota ao log do sistema (`journalctl -t rotinas`). É a forma
que o passo 9 de `procedimento-de-corte.md` dá às outras 21, e a conferência do
segundo bloco dele a confere. O `SEU_TOKEN` é o `cron.token` do `local.php` de
produção — o novo, da rotação. **Sem `%` na linha:** o cron o troca por fim de
linha.

### A dos convênios

**01:30**, diariamente. A escolha tem motivo:

| | |
|---|---|
| De madrugada | são 616 convênios, custo desprezível, mas nada ganha rodando no horário de atendimento |
| **01:30** | entre `cupons/cuponsValidarVencimento` (01:20) e `relatoriosAtendimentosAtualizar` (02:10), sem disputar com nenhuma |
| `-m 300` | cinco minutos é folgado para 616 linhas e curto para acidente |
| Diária, não horária | data de contrato muda uma vez por ano, não por hora |

**Não agende entre 21:00 e 00:00.** A rotina compara `date("Y-m-d")` do PHP com
a data gravada, e nessa faixa PHP e banco podem discordar sobre que dia é
(**D42**) — o que faria um contrato ser marcado ou desmarcado um dia antes ou
depois. Às 01:30 a questão não existe.

**O domínio é o raiz, `franquiabv.com.br`, nas três** (a resposta 194 (a) do
dono; até 07/11/2026 a linha dizia `SEU_DOMINIO`). O crontab de hoje mistura
`www` e o raiz, e o `curl` está sem `-L`: se um dia houver redirecionamento, a
linha recebe o 301 e a rotina não roda. O passo 9 do procedimento põe as outras
21 no raiz, e a conferência dele recusa o `www`.

### As duas limpezas de dentro do MySQL (o D26)

Escritas em 07/11/2026 (a fatia 113), pelas respostas 127 (a) — *"As duas viram
rotinas do crontab, com os prazos de hoje"* —, 184 (a) — a de 24 h poupa a
mensagem que falhou, que sai em 7 dias — e 195 (a) — as duas entram na etapa 1
do corte, e o evento de hora em hora se desliga na mesma janela
(`procedimento-de-corte-etapa-1.md`, o passo 12; a volta o religa).

| | `pushAppLimpar` | `agendasExclusoesLimpar` |
|---|---|---|
| O que apaga | a mensagem da fila (`push_app`) que **espera** há mais de 24 horas; a que falhou (`falhou:`) fica, e sai em 7 dias contados do pedido pela rotina da própria fila | o horário desmarcado (`agendas_exclusoes`) com mais de 60 dias, pela `data_exclusao` |
| Quando | **aos 35 minutos de cada hora**, como o evento `limpar_push_app_24h` que ela substitui. A sobra das filas do parabéns (06:00) e do "sentimos sua falta" (07:00) completa 24 horas entre 06:00 e 07:10 do dia seguinte, e a rodada das 06:35 ou das 07:35 a tira antes do envio das 10:00, como hoje (o **D489**; a resposta 186 (c), "Como está") | **02:40**, uma vez por dia, como o evento dela: depois de `relatoriosAtendimentosAtualizar` (02:10, até 10 minutos) e antes de `unidadesTicketMedioAtualizar` (04:10) |
| `-m 300` | a rodada de hora em hora apaga a sobra de uma hora; a primeira, depois da janela, a de um dia, em lotes de 5.000 | ~340 linhas por dia na cópia; a primeira, o que o apagador que a produção tem hoje deixar (o **D26**), em lotes de 5.000 |
| A resposta | `{"sucesso": true}`; `{"sucesso": false, "mensagem": "Processo já em execução."}` com a trava detida | a mesma |
| O log das rotinas | `pushAppLimpar - apagou N ...`, só quando apagou alguma | `agendasExclusoesLimpar - apagou N ...`, todo dia, com zero também |

**Em lotes, e não num DELETE só:** a produção tem três réplicas, e um DELETE
grande vira um evento só no binlog, que atrasa as três (o **D26**). **Fora das
21:00 às 00:00?** O **D42** não as alcança: o prazo e a data da linha vêm os
dois do relógio do PHP (a `PushApp` nasce com a hora do PHP, e o
`AgendaService::arquivarAgendaExclusao` grava a `data_exclusao` também do PHP), e
por isso a de hora em hora pode passar pela faixa.

**Não ensaiadas na produção nem no BVIP:** as duas rodaram só na suíte, nos dois
bancos (`tests/Caracterizacao/D26LimpezasNoCrontabTest.php`); a linha do crontab,
no ensaio do passo 9 (a forma). A primeira rodada de verdade é a da janela.

---

## O que muda com ela no ar

A rotina marca quais convênios passaram da data de contrato. Hoje há **18
nessa situação e nenhum marcado**, o mais antigo há 1.933 dias — e o filtro
"contrato vencido" da listagem devolve sempre lista vazia, dizendo a quem
procura contratos a renovar que está tudo em dia.

Na primeira execução, esses 18 aparecem de uma vez. **Isso é o esperado, não um
defeito** — vale avisar quem cuida dos contratos antes, para o salto não ser
lido como erro do sistema.

**O que ela NÃO faz, e não deve passar a fazer:** encerrar a vigência. A
vigência do convênio segue até que alguma parte cancele; a data é referência
comercial. O desconto na venda não consulta este sinalizador. Ver **D50**.

---

## A segunda rotina não está aqui, e o motivo importa

`yungasNpsAtualizar` era a outra candidata a agendamento (**D43**). Ela **não
deveria ser agendada**, porque não faria nada: o corpo do método tinha **27
linhas e as 27 estavam comentadas**.

**Resolvida em 31/08/2026, por decisão sua: removida.** O método, a ação do
controlador e a rota saíram. Não há mais o que agendar nem o que decidir — e o
parágrafo abaixo fica como registro de por que a remoção foi a saída certa.

Não é uma rotina esquecida — é uma rotina **desligada**, com a rota deixada para
trás. Agendá-la produziria um `sucesso: true` a cada execução, fecharia o D43 na
aparência e não sincronizaria um único NPS.

A integração Yungas está viva: `yungasFaturamentoAtualizar` tem 49 linhas
ativas e roda às 23:40, e `yungasAutenticacao` funciona. Foi só a metade do NPS
que alguém desligou, e o código que a desligou não está no repositório — o
histórico começa na importação inicial.

**A decisão é de negócio, não de engenharia:**

| Se | Então |
|---|---|
| a Yungas ainda espera o NPS | descomentar, testar contra a API deles, e só então agendar |
| não espera mais | remover o método, a ação e a rota — hoje é superfície sem função |

Enquanto não se decide, `scripts/conferir-agendamento.php` continua acusando —
e isso está certo. O acusado é real.
