Pular para o conteúdo principal
← Voltar ao changelog

Changelog - 31/05/2026

Semana 25–31/05 — Governança: CODEOWNERS em todo repo + gate de autorização de deploy

  • CODEOWNERS chega a todos os repositórios de front045206ec2 no front-web-base, e93a938 no front-cactus-core, 296deb8 no front-ops, 333c4f3 no front-tests-e2e, da59ea2 no front-service-api e dfd3af6 no front-service-api-dev-proxy. A regra default é * apontando pro time de codeowners de front — qualquer arquivo do repositório passa a exigir review de quem está no time, e adicionar codeowner virou uma mudança de membro de team no GitHub em vez de edição de arquivo.
  • .github/ endurecido com owners explícitos848c853a6. A ownership default segue no time, mas .github/ (workflows e o próprio CODEOWNERS) passa a exigir aprovação nominal de dois owners específicos. A justificativa está no commit: arquivos de governança não deveriam ser mergeáveis só porque qualquer membro do time aprovou — uma aprovação futura maliciosa ou distraída bastaria pra neutralizar a proteção. A sintaxe é gitignore-like com last-match-wins: a regra mais específica abaixo substitui a default acima, não faz união.
  • Gate de autorização para workflow_dispatch de deploy — front-ops 5dbdcb5 adiciona o campo opcional deploy_protection.required_team no deploy.yml de cada ambiente. Quando presente, deploys manuais via workflow_dispatch naquele ambiente ficam restritos a membros do time nomeado; push (auto-deploy) nunca é gateado por esse campo e continua disparando exatamente como antes. A implementação consulta GET /orgs/.../teams/.../memberships/<actor> com um token que passou a carregar read:org, e falha rápido com erro linkando o time. Aplicado aos 13 ambientes de produção do web-base — todos sem prefixo stage-/dev-/performance-, menos os dois ambientes internos.
  • Gate movido pro job check3d645e8 (mais o gêmeo ee5751e) reposiciona a verificação: o gate é uma pré-condição, como a checagem de elegibilidade de auto-deploy que já existia, então pertence ao job que valida inputs e resolve config. O ganho concreto é a forma da falha na UI — vira check ❌ → deploy skipped em vez de check ✅ → deploy ❌, que sugeria falsamente que o deploy tinha sido tentado. Sem mudança de comportamento: mesmo schema de campo, mesmas checagens, mesmas mensagens de erro.

FTD-cashback (Dark Freedom D0): endurecimento da máquina de estados

  • Cinco falhas corrigidas de uma vezfee37fd6a. O bug mais grave era o watcher morrendo ao navegar entre jogos elegíveis: gameId não estava nos deps do efeito de init e isEligible é boolean, então ao trocar de um jogo elegível pra outro o valor permanecia true (mesma primitiva), o React não re-executava o efeito, o cleanup de troca de jogo matava o watcher e nada o reiniciava — o fluxo ficava inativo pra todos os jogos seguintes da sessão. Também foi corrigido um gate inalcançável no fluxo sequencial (o .finally() de um gate anterior zerava o watcher incondicionalmente e o gate seguinte tinha if (!hasBonusWatcher) return como primeiro guard), uma race condition entre dois gates disparando ao mesmo tempo quando o saldo já estava abaixo do limiar na abertura do jogo (o modal exibido dependia de qual API respondia por último) e o .finally() de um jogo antigo matando o watcher do jogo novo. da2e788f4 reverte parte dos deps do watcher em seguida.
  • Cache de elegibilidade em sessionStorage removido2e0856439. A chave ftd_cashback_eligible guardava o resultado do Dark Verifier e o pulava nas aberturas seguintes de jogo, o que abria brecha: manipular o storage bypassava a verificação e podia acionar o fluxo de cashback indevidamente. isVerified/markVerified e a chave saíram inteiros — o Dark Verifier passa a ser consultado fresh a cada abertura, e o backend é a única fonte de verdade. Os outros flags de sessão (sent, shown) ficam, porque servem como anti-double-send e rastreio de estado de UI, não como gate de segurança.
  • Sessão queimada ao sair de jogo elegível4c7a879df implementa a regra de produto: uma vez que o player navega pra fora de um jogo elegível, perde o bônus D0 pelo resto da sessão do browser, sem exceção mesmo que o próximo jogo também seja elegível. Vira SESSION_BURNED_KEY em sessionStorage (sobrevive a reload na mesma aba, limpo no logout), um ref de estado anterior detectando a saída, guard no topo do efeito de init bloqueando re-execução e um segundo guard pós-await dentro do run() cobrindo a race de jogo elegível A em voo enquanto o player navega pro elegível B.
  • Modal gateado no body booleano do Dark Freedom35d372a19. O proxy só checava o status HTTP, mas o endpoint responde HTTP 200 tanto no sucesso quanto na falha, diferenciando apenas pelo body booleano true/false — o modal de bônus aparecia mesmo quando a resposta era false. Agora o proxy inspeciona o body e só retorna { ok: true } quando ele é estritamente true; false, body não-booleano ou JSON malformado devolvem { ok: false } e o modal não aparece.
  • Endpoints D0 migrados pra sa-east-158f39be4e: dark-verifier e dark-freedom apontavam pra eu-central-1; os defaults foram migrados, cobrindo as marcas que usam o default (a cassino já estava migrada por override próprio).
  • Nova tabela de tiers FTD → cashbacke72381bee substitui a grade default por 36 faixas contíguas de R$ 5 até infinito (.00 no máximo virando .01 no mínimo seguinte), com o último tier como catch-all: FTD acima de R$ 3.000 resolve pro bônus máximo de R$ 800. Vale pra 7k, cassino e vera, que não fazem override de bonusTiers.
  • Limpeza de código mortof29b99b8c remove o endpoint de logs e suas chamadas, e b8b8b1813 derruba um override redundante da cassino.

Auth e recuperação de senha por país

  • ng-7k-bet: recuperação por e-mail ao lado de documentobbfa3a4b7 muda options de ['document'] pra ['email', 'document'] e o default pra email, com a descrição do modal atualizada; o documento segue disponível no seletor de abas. 36e4ec172 faz o mesmo pra fi-7k-bet.
  • forceRegisterValidatePhone num vai-e-volta16cb7e286 remove a flag de dev, 5df7c150b reverte a remoção, 1f783acba desliga a flag no ng-7k-bet e ab063a4a5 liga de novo, tudo dentro de três dias. Fica registrado como o que foi: uma flag de QA sendo calibrada em ambiente real, não uma entrega.
  • 4991780d6 esconde a edição de nickname no painel de usuário até o SDK do Smartico ser corrigido.

Proteção do jogador e área do usuário

  • Flag protectionPageEnabledabf38eb05 adiciona o campo a AccountFeatureFlags (default true) e esconde a aba "Proteção" do UserTabNavigation, do UserPanelAccountMenu e da seção de conta restrita na sidebar quando a marca opta por fora — mesmo padrão do filtro de incomeReport que já existia. Um loader novo em /user/protection responde 404 quando a flag é falsa, cobrindo acesso direto por URL (bookmark, e-mail, digitação) em todos os aliases localizados (/usuario/protecoes, /jugador/protecciones, /player/protection). A cl-bet7k-com opta por fora porque no Chile a autoexclusão é tratada por canal centralizado do regulador, não no app; os outros overrides propagam true pra satisfazer o contrato de substituição de arquivo.
  • Autoexclusão da betpontobet via chat humanof8e372627 dá paridade com 7k / cassino / vera trocando o formulário local direto pelo modo "suporte primeiro com trigger no logo": o usuário vê primeiro o botão de falar com o suporte, que abre o iframe de chat humano dedicado, e tem escape via duplo clique no logo da marca pra revelar o formulário completo. A snippet key é específica da marca, com workflow humano-only configurado no admin.
  • 69ab6389b corrige o easter-egg do logo que ficava invisível em SVG que só declara viewBox, e 17509cbd2 impede o modal de resgate de recompensas de abrir vazio, adicionando feedback de erro.

Gamificação

  • Ordenação por relevância virou opt-inc88159180. A ordenação por mais participantes/featured, entregue na semana anterior, foi liberada por decisão de produto apenas para as três marcas BR. Nasce a flag gamificationRelevanceSort em AppFeatureFlags, com default undefined (= desligado) e não declarada em app/config/features/features.ts, seguindo o padrão opt-in em que só quem liga declara. O TournamentsSection só aplica a ordenação com a flag ligada; as demais marcas seguem no comportamento legado.
  • 833a8fef5 força cache: "no-store" no GET de tipo de missão do álbum, 07c44b0bf devolve o X do header e cola o footer no rodapé na página de termos, 48640aab4 remove a seção "Recompensa do dia" dessa mesma página e 257d53712 renomeia album.cache.server pra album.service.server — o arquivo tinha deixado de ser só cache.

SEO

  • Hreflang localizado pros paths da marca irmã916422e3d. As entradas de <link rel="alternate"> concatenavam o pathname atual em cada origin irmão, então 7k.bet.br/cassino/ao-vivo emitia o equivalente /cassino/ao-vivo no domínio chileno — que usa /casino/en-vivo e portanto quebrava. Foi adicionado um campo opcional routePaths em HreflangEntry carregando o mapa completo de paths da marca irmã: o _layout.tsx faz reverse-match do pathname atual contra o routePaths local via matchPath (ordenado por especificidade de segmento literal, e sem splat primeiro) e re-resolve a mesma route key com o padrão da irmã via generatePath, caindo no pathname local cru quando nenhuma key casa (slugs dinâmicos de conteúdo). As quatro marcas irmãs da família 7k (BR / CL / FI / NG) passam a anexar os routePaths das outras em cada entrada que não é auto-referência, e os mapas compartilhados vivem em app/config/seo/sibling-route-paths-7k.ts — porque o plugin de brand overrides é substituição de arquivo e um build de uma marca não pode importar a config de outra. Marcas de país único ficam intocadas, já que só se auto-referenciam. Nesta passada o x-default de CL/FI/NG usava o mapa BR; isso foi revertido na semana seguinte.

Renderização server-side

  • Detalhe de jogo voltou a fazer SSR8646480a8. Os helpers de session storage (getSessionItem / setSessionItem / removeSessionItem) estavam declarados num arquivo .client.ts, o que os tirava do bundle do servidor — mas todas as funções já guardavam typeof sessionStorage === "undefined", ou seja, foram desenhadas pra serem SSR-safe. O sufixo era uma classificação errada. Nas marcas com FTD Cashback ligado, um load direto ou F5 em /cassino/jogar/:provider/:game montava o provider ativo, o useFtdCashbackFlow chamava isSessionBurned() durante o SSR e caía num TypeError: (void 0) is not a function500. A betpontobet não tropeçava porque a flag é undefined lá e o provider retornava null antes. O fix renomeia o arquivo pra session-storage.ts e atualiza os quatro consumidores (armazenamento de ftd-cashback, de ftd-offer, topbar e notificação de baixo), com teste de regressão cobrindo o caminho SSR e o do client.

Brand Overrides

  • betpontobet-bet-br: semana mais movimentada da marca — links legais extras (Denúncias, Privacidade, Ouvidoria) adicionados em 797e86ec5 / ab18fe49a e depois reduzidos a apenas Ouvidoria em 09045d08a / 591fe84a3; help center apontado pro subdomínio de atendimento (495f5d7df, b6c2b44eb); link "Regras de Apostas" na sidebar de esportes (8ca5e48ab — que também corrigiu o SidebarNarrow, onde o item renderizava como Link com to="", visualmente quebrado e com clique morto; os outros três variants já tinham o branch de onActionClick); autoexclusão por chat humano; abas de provider Pix; valor default de depósito.
  • 7k-bet-br: catálogo de figurinhas do álbum atualizado pra 133 entradas (db032a87b), label "Sportsbook" renomeada pra "Esporte" no gate de escolha de trilha (484c482c0), modal informativo "Como funcionam as Cartas Diamante" habilitado (74e722917), assets visuais do estado deslogado atualizados (be43d6164), rodapé de página do álbum removido (564ba9c7b), a palavra "Copa" retirada de todas as strings user-facing (e4ec5195f), URLs da estratégia de roleta premium corrigidas (8e8c15929) e uma campanha de minigame com UTM própria (66268b9e1).
  • vera-bet-br: banner de urgência pré-FTD, redesenhado com a arte do Fortune Rabbit.
  • cl-bet7k-com: página de proteção do jogador desligada — primeira marca a usar protectionPageEnabled: false.
  • pb-bet: link de Ouvidoria adicionado ao rodapé (2a677f85f).
  • 7k, cassino e vera: ordenação de gamificação por relevância ligada explicitamente; nova tabela de tiers de FTD → cashback.

Pagamentos

  • Banner de urgência pré-FTD no modal de depósito9f0aac235 mostra um banner laranja com countdown de 5 minutos no topo do modal pra usuários sem primeiro depósito; depois de expirar ou de fazer o FTD, ele não aparece mais. O timer é persistido em localStorage sob deposit_urgency_start, a feature fica atrás da flag depositUrgencyBanner em AppFeatureFlags (desligada por default, ligada no override da vera-bet-br), e as cores vêm de config pra não exigir restart do dev server ao trocar de paleta. Os tokens ftd-offer tiveram que ser adicionados ao colors.ts da marca porque override é substituição de arquivo — sem o bloco completo o FtdOfferModal e o FtdCashback quebram. 04e5dc2a2 redesenha o banner da vera com a arte do Fortune Rabbit.
  • Valor default de R$ 50 no modal de depósito101f9ad45 alinha cassino, vera e betpontobet ao comportamento do 7k: abrir o modal sem valor na URL pré-seleciona R$ 50, e o override por ?value=XXX continua tendo prioridade.
  • Abas de provider Pix expostas na betpontobet035e315b9 adiciona override de paymentsConfig com groupPixProviders: false, desligando o agrupamento Pix-only default do base. Providers Pix-like (efibank, pixtopay, …) voltam a aparecer como cards individuais no MethodSelector, e o slug submetido ao BFF passa a ser o do provider escolhido pelo usuário — mesmo padrão já aplicado em 7k, cassino e vera.
  • Trigger ?deposit=1[&value=] restauradodd0310ee2. O useEffect único do DefaultLayout que tratava o contrato legado corria contra si mesmo: chamar navigate({ replace: true }) pra limpar os params mutava location.search, que era dependência do próprio efeito, retriggerava o efeito e a fase de cleanup do React executava clearTimeout() no openPaymentModal("deposit") pendente antes do timer de 500ms disparar. Resultado: o modal nunca abria pra usuário autenticado. O caminho deslogado parecia funcionar só porque o openAuthModal era despachado sincronicamente sem cleanup. Regressão introduzida em abril, quando a limpeza de URL foi adicionada junto com o pré-preenchimento de ?value=.

Infra / CI/CD

  • Provisionamento de KV tolerante a soluços da API do Cloudflare — front-ops 28ec511. O passo anterior fazia npx wrangler kv namespace list | jq …, o que era frágil sob qualquer degradação do control plane: se o stdout do wrangler trouxesse qualquer ruído não-JSON (banner informativo, página HTML de erro parcial, aviso de deprecação), o jq falhava, a variável ficava vazia e o passo tentava criar um namespace que já existia; a criação saía com código não-zero, o set -e matava o script antes do echo de diagnóstico e o runner reportava apenas "exit code 1" sem contexto. Virou uma pequena máquina de estados: tenta o LIST, se não parsear tenta extrair o bloco [...] entre a primeira e a última linha de colchete (cobre banners que envolvem o JSON), e só então cai no CREATE.
  • Regressão do próprio hardening corrigidaa6e4e6a. O passo endurecido tinha trocado 2>/dev/null por 2>&1 pra capturar erros no diagnóstico, e isso trouxe um efeito colateral: o npx wrangler emite um aviso de config depreciada no stderr em todo deploy, que com 2>&1 passava a aparecer no topo da captura, quebrando o jq e fazendo o passo cair indevidamente no CREATE — desperdiçando um round-trip de "namespace já existe" em cada deploy de ambiente em estado estável. A versão anterior funcionava por acidente: o 2>/dev/null escondia tanto os erros do wrangler quanto o aviso. O fix mantém os dois: stderr vai pra arquivo temporário (stdout chega limpo ao jq) e o conteúdo capturado é impresso nos avisos e erros. b3484ca fez um rollback temporário pra teste de diagnóstico no meio do processo.
  • front-ops: default branch do ng-7k-bet alterada (ac9cc3a).