Backlog Unificado
Projeto: RioNoTeatro. Fonte principal: /www/wwwroot/rionoteatro.com.br/docs/BACKLOG.md.
Modo read-only: ações de escrita ficam disponíveis apenas para o Cérebro.
Sem itens pendentes em /www/wwwroot/rionoteatro.com.br/docs/BACKLOG.md.
Especificações Disponíveis (fora da fila pendente)
- BK-136
- BK-137
- BK-138
- BK-147
- BK-148
- BK-149
- BK-150
- BK-151
- BK-156
- BK-158
- BK-159
- BK-160
- BK-161
- BK-162
- BK-163
- BK-164
- BK-165
- BK-166
- BK-170
- BK-171
- BK-172
- BK-177
- BK-183
- BK-186
- BK-187
- BK-189
- BK-190
- BK-191
- BK-192
- BK-193
- BK-195
- BK-196
- BK-197
- BK-198
- BK-199
- BK-201
- BK-205
- BK-207
- BK-208
- BK-209
- BK-210
- BK-211
- BK-212
- BK-213
- BK-214
- BK-215
- BK-216
- BK-217
- BK-218
- BK-219
- BK-220
- BK-221
- BK-229
- BK-230
- BK-231
- BK-232
- BK-233
- BK-234
- BK-235
- BK-236
- BK-239
- BK-240
- BK-241
- BK-242
- BK-243
- BK-244
- BK-245
- BK-246
- BK-248
- BK-249
- BK-250
- BK-251
- BK-252
- BK-253
- BK-254
- BK-255
- BK-256
- BK-257
- BK-258
- BK-259
- BK-260
- BK-261
- BK-262
- BK-263
- BK-264
- BK-265
- BK-266
- BK-267
- BK-268
- BK-269
- BK-270
- BK-271
- BK-272
- BK-275
- BK-276
- BK-277
- BK-278
- BK-279
- BK-280
- BK-295
- BK-313
- BK-341
- BK-345
- BK-346
Detalhe do BK Selecionado
BK-346 — Ruído de alertas de push para o admin
Status: entregue em 28/07/2026 (itens 1–5). Item 6 (limpeza da tabela) pendente de execução manual.
Origem: análise das notificações push que o admin vinha recebendo.
Relacionado: BK-345 (Web Push), BK-341 (guardião de integridade MP).
Problema
O dono recebia ~24 push/dia, quase todos do mesmo alerta. A investigação encontrou três causas
empilhadas e, no caminho, um problema de negócio maior que o ruído em si.
Causa 1 — o push furava o rate limit do WhatsApp
enviar_push_admin_from_whatsapp() é chamado no topo de enviar_whatsapp_admin()
(includes/whatsapp_helper.php:1628), antes do enviar_whatsapp() que aplica o rate limit de 24h.
Alertas que no WhatsApp saíam 1x/dia iam no push de hora em hora. O log confirmava: a cada hora
saía BLOQUEIO (RATE LIMIT 24H) no WhatsApp e o push ia assim mesmo.
Causa 2 — o alerta dominante era falso positivo, há 5 meses
admin/cron/monitor_story_roi.php (cron 7 ) lia um caminho hardcoded
/www/server/cron/ecef0b533e9b16166be606a34f900a72.log, congelado desde 05/03/2026 porque o cron
do Facebook virou rnt_sync_facebook_planner / rnt_sync_facebook_dispatcher. O check ficou preso em
error (que usa force=true e throttle de 900s) por ~5 meses.
Causa 3 — o log de push não permitia auditoria
push_dispatch.log recebia só a saída crua do sender (Enviadas: 1 | ...), sem timestamp e sem
título. Foi preciso reconstruir o histórico cruzando com admin/logs/debug.log.
Achado de fundo — o monitor vigia uma iniciativa que não decolou
story_redirect_click tem 11 eventos em toda a série histórica, o último em 10/04/2026.
O go.php, único arquivo que grava esse evento, está órfão: nenhum código do site gera links
apontando pra ele. O que circula hoje é o short.php, cujos cliques são utm_medium=social
(Facebook feed) e crm (WhatsApp) — nenhum com medium story.
E o gargalo real do canal aparece em social_post_queue: nos últimos 30 dias há **42 stories em
pending_approval (22 facebook_story + 20 instagram_story) contra 3 publicados**. Os stories
não estão sendo publicados porque a aprovação não acontece — não porque o tracking quebrou.
Consequência: os três checks do monitor descreviam estado normal, então ele ficava em warn/error
permanente. Corrigir só a causa 2 derrubaria de ~24 para ~1 push/dia, mas manteria um alerta diário
sem nada acionável — o tipo de alerta que se para de ler.
O que foi feito
- Dedup próprio no push de admin —
includes/push_admin_helper.php. Arquivo
admin/logs/push_admin_dedup.json (www:www 664, flock, limpeza automática), janela padrão
86400s em RNT_PUSH_ADMIN_DEDUP_PADRAO, 4º parâmetro $dedup_seconds em enviar_push_admin()
(0 desativa). Fail-open: erro de I/O nunca silencia alerta.
A chave normaliza data e hora antes do sha1 (_push_admin_dedup_chave) — sem isso o dedup
seria inútil, porque os alertas trazem Data: dd/mm/YYYY HH:ii:ss no corpo. O rate limit do
WhatsApp escapa disso por acidente: usa md5($numero . substr($mensagem, -100)) e a data cai no
meio da mensagem.
- Caminho do log do cron —
monitor_story_roi.php. Lista de candidatos (dispatcher → planner →
log antigo) escolhendo o de mtime mais recente, em vez de caminho fixo. O cron já foi renomeado
uma vez e foi exatamente isso que criou o falso positivo silencioso.
- Log auditável — cada linha agora sai
[data] admin titulo=... => Enviadas: N | Expiradas: 0 | Falhas: 0, e supressões viram
admin SUPRIMIDO (dedup 86400s) titulo=.... Como o disparo é em background, contexto e resultado
são montados numa escrita só (sh -c capturando $OUT); logar em duas etapas embaralharia o log
quando dois alertas saem juntos.
- Notificação por transição de estado —
monitor_story_roi.php. Só avisa quando o status
muda (piorou, ou normalizou), com o último estado em admin/logs/monitor_story_roi_state.json.
Estado estável = silêncio. Primeira execução sem estado salvo só avisa se já nascer em error,
pra não ficar mudo caso o arquivo se perca durante uma incidência real. O JSON completo continua
indo pro log do cron a cada run: nada deixa de ser observável, só para de ser empurrado.
- Dois checks ruidosos ajustados:
roi_story_clicks_24h→roi_story_clicks_7d: janela de 7 dias, e "zero clique" só é warn se
houve story publicado no período (consulta social_post_queue). Sem post publicado, zero
clique é o esperado.
campaign_ads_story: campanha ADS ativa é estado normal do negócio. Agora só alerta na
contaminação real — ads e orgânico dividindo o mesmo utm_campaign.
- Allowlist no campo
evento—action.php, casesave_tracking. O nome do evento vinha do
cliente e ia direto pro banco. Não era brecha de SQL (escapado com mysqli_real_escape_string,
e peca_id/valor passam por intval/floatval), mas qualquer string virava uma linha nova:
scanners em 12/01 e 11/04/2026 gravaram 185 linhas com payloads (' OR 5*5=25 --,
${@print(md5(31337))}, c:/windows/win.ini) e inflaram a tabela para 110 nomes de evento
distintos contra ~9 reais. api/track-event.php já tinha allowlist; action.php não.
Pendência
A limpeza das 185 linhas de lixo não foi executada (o DELETE foi bloqueado pelo classificador de
permissões do agente). O backup das linhas já está em
/root/.rnt_security/cliente_tracking_lixo_20260729_074504.tsv (600). SQL para rodar:
```sql
DELETE FROM cliente_tracking
WHERE evento NOT IN (
'view_item','view_list','date_selected','add_to_cart','begin_checkout',
'purchase','utm_detected','shortlink_redirect_click','story_redirect_click'
);
```
Sem risco de recontaminação: a allowlist do item 6 já está ativa.
Decisão em aberto — destino do go.php
O canal de story está no pior dos dois mundos: paga o custo do alerta sem entregar o dado. Ou se
reconecta (o dispatcher passa a gerar o link do story via rastreador com utm_medium=story, e o ROI
de story passa a existir), ou se aposenta go.php junto com o monitor. Antes disso, porém, o gargalo
a resolver é o dos 42 stories parados em pending_approval — sem publicação não há clique para
medir, e nenhum ajuste de tracking muda isso.
Verificação
- Monitor: status geral saiu de
errorparaok, todos os 9 checks OK. - Transição:
ok→okem duas rodadas seguidas = zero push;error→ok= 1 pushNORMALIZADO. - Dedup: dois envios idênticos seguidos = 1 entregue + 1
SUPRIMIDO; alerta com conteúdo novo passa;
pedidos #1001 vs #1002 não colidem.
- Guardião MP:
action.phpé monitorado —--checkacusouDRIFT,--seal,--checkOK.
Bug lateral encontrado
O php.ini da VPS está com date.timezone = PRC (UTC+8), 11h à frente do sistema (-03). Os
scripts do site escapam porque config/functions_new.php:9 faz
date_default_timezone_set('America/Sao_Paulo'), mas o helper de push roda em contextos que não
carregam esse config — o log saiu com 2026-07-29 07:31 enquanto o sistema marcava 20:31.
Corrigido com DateTime + timezone explícito em _push_admin_agora(), sem mexer no estado global do
processo do chamador. Vale para qualquer script PHP CLI nessa VPS.
Backups
includes/push_admin_helper.php.bkp_original_2026-07-28_20-28-27_dedup-logadmin/cron/monitor_story_roi.php.bkp_original_2026-07-28_20-28-27_fix-cronlogadmin/cron/monitor_story_roi.php.bkp_original_2026-07-28_20-40-*_transicao-checksaction.php.bkp_original_2026-07-28_20-4*_allowlist-evento-trackinggo.php.bkp_original_2026-07-28_20-40-*_allowlist-evento(go.php acabou não sendo alterado)