Changelog - 28/06/2026
Semana de 22/06 a 28/06, na branch main-7k do front-web-base.
Semana 22–28/06 — Consent Mode v2: default invertido pra granted e consent cosmético como padrão
- O baseline de boot do Google Consent Mode v2 foi invertido pra
granted(16bf59fee). Isto reverte o comportamento descrito no changelog de 23/04/2026. A política antiga emitiagtag('consent','default', { ...tudo denied, wait_for_update: 500 })e só depois, se houvesse cookie salvo, aplicava um update. A nova emitegtag('consent','default', { ...tudo granted })sempre, semwait_for_update. O único caminho que ainda rebaixa sinais écookieConsentCosmeticOnly === falsee cookie salvo indicando rejeição total, caso em que oinitGTMdispara um update imediato pradenied. Matriz implementada: cosméticotrue→granted(qualquer cookie ignorado); cosméticofalsesem cookie ou com aceite →granted; cosméticofalsecom rejeição total → defaultgranted+ update imediato pradenied. Arquivos:app/utils/metrics/consent.ts(injectConsentDefaultscomopts.applyDenied) eapp/utils/metrics/gtm.ts(decideapplyDenied = !cosmetic && userRejectedAll), com 4 casos de teste. A justificativa registrada no commit é paridade com o app Nuxt legado, que nunca gateava tracking, e a eliminação da janela cega de 500ms dowait_for_update, apontada como causa suspeita da queda de performance em Google Ads e Meta Ads após a migração. cookieConsentCosmeticOnlypassa a ter defaulttrueno base (be0518d91). Antes o default global erafalse, então brand que não declarasse a flag honrava a rejeição do usuário. As 7 brands que já declaravamtrueseguem idênticas; as 8 que herdavamfalse(casateste-com,donald-bet-br,pb-bet,ph-state77-com,pt-state77-com,rj-bet,state77-com,x2b-bet) passaram a declararfalseexplicitamente no próprio override — comportamento preservado bit a bit, com o ganho de que cada override é agora a fonte autoritativa da política de consent daquela brand, sem depender do default do base.- Nota de revisão pendente: as duas mudanças acima descrevem o que o código faz hoje. A política resultante — consent concedido por padrão, tratamento cosmético como default e ausência de botão de rejeição total no banner — ainda depende de confirmação do responsável por LGPD antes de ser publicada como política oficial. Registrado aqui como fato técnico, não como posição de conformidade.
- Eco do
gtagduplicando eventos dodataLayercorrigido emea9b09a68.
Resiliência de navegação no Safari
- Fog-of-war de descoberta de rota desligada (
5f5640b7f): os logs de Cloudflare dabetpontobet(Safari) mostraram que a request que mais falha é/__manifest?paths=..., a descoberta lazy de rotas do React Router 7 — 16 das falhas amostradas, todas "Network connection lost" comoutcome=okno worker, ou seja, o Safari derrubando a conexão no meio da resposta em navegação rápida. Páginas densas em links, como o lobby de cassino, disparam um/__manifestcom 15+ paths, o que explica o cassino reproduzir mais.routeDiscovery: { mode: "initial" }manda o manifest completo no documento inicial, eliminando as requests e um round-trip de navegação; os módulos de rota seguem code-split. - Recuperação transparente em três camadas —
979b34850faz retry de falhas transitórias na camada de fetch;0dbf409c0cobre o single-fetch (.data) com um reload de documento inteiro guardado porsessionStoragenoErrorBoundary, mais umtry/catchno topo do fetch handler deworkers/app.ts(antes, throw não tratado no SSR virava 500 cru sem log, uma falha estrutural de observabilidade);3d7280287move o backstop pra dentro doErrorPagee substitui a tentativa anterior, que vivia só noErrorBoundarydo root: como o_layouttemErrorBoundarypróprio e o React Router entrega o erro ao boundary mais próximo, erro de loader em rota interna caía no_layoute o backstop nunca disparava. OErrorPageé renderizado pelos dois boundaries. Não recarrega em 404/500 real.
Performance da home e isolamento de fontes
- Pass de PSI/Core Web Vitals na home (
cf38712b1): novoscheduleThirdPartyDeferred(idle do browser, ou primeira interação, ou timeout de segurança) tira Pendo, Zendesk, Hotjar, Clarity, Incognia e o selo Reclame Aqui do caminho crítico do load, mantendo GTM e pixels de conversão eager pra não perder evento. O banner da home passa a renderizar um<picture>com<source>mobile capado em 400px, então telefone nunca baixa banner de desktop independente do DPR, maisqualityconfigurável (default 70, era 80 global) propagado pro base e pros overrides. O preload de thumbnail.mp4de card de jogo vira viewport-gated porIntersectionObserveremuseCustomThumb— cerca de 3,6 MB de vídeo off-screen deixa de baixar no load inicial. E oroottroca o script de escape-hatchinit2peloinit4: oinit2recarregava a página inteira em toda visita fresca ou lenta pelo triggerduration>200de/api/clear-cache— é o "redirect de 32s" que o PSI apontava, o double-load confirmado em trace de DevTools. Oinit4é inerte por padrão. - Fontes de brand isoladas por override (
68ebd706b):~/config/theme/font-preloadse~/styles/brand-fonts.css?urlpassam a ser brand-resolvidos como qualquer import~/. O base traz Montserrat; adonald-bet-brsobrescreve os dois arquivos com Inter, então oswoff2de Inter deixam de entrar no bundle das outras brands. OresolveIddobrandOverridesPluginpassou a tirar a query (?url) antes do lookup de override e reanexá-la depois, pra imports?urlresolverem tanto no override quanto no base. O__BRAND_KEY__do define do Vite ficou sem uso e saiu.
Bolão 7K — API real, redesign do card e depois desligado
- A API real entrou e o mock saiu (
d6b27670c): a API de jogos do dia é a mesma API coreana usada pelo sweepstakes, então o service passou a reusarSWEEPSTAKES_API_URLeSWEEPSTAKES_BRAND_IDe a chamarGET /games/sweepstakes/daily-gamescom headerbrand-id.ApiMatchfoi ajustado pro shape real (camelCase,isGameOver,startDatesem timezone) eMatch.gameStartedvirougameOver. - Card de vitrine na home do 7k, iterado cerca de 15 vezes na semana —
58ff56d62ec133ea5f0criam o card acima de torneios,7d2659729o move pra acima de "Cassino ao vivo",1512c0193acerta o cabeçalho conforme o doc,6c84c5a47troca o CTA peloCtaButtonpadrão da casa,a5412db7frenomeia pra "Dê seu palpite" e adiciona um segundo CTA "Apostar agora" (95a75d8f1restila esse CTA e trata jogo expirado por horário),f9a55533cdestaca o próximo jogo e5b379e5e9remove esse destaque,1d1e5f4b7põe borda em gradiente,9d77e47a3monta layout de 2 painéis no desktop,189e94788e68e0deae9tratam clique e hover das partidas. - Gateado por feature flag e então desativado na home —
b49605873faz o card respeitar a mesma flag de runtimefeBolao7kque já controla menu lateral, agregador e bottom nav (antes dependia só do kill-switch de build-timebolaoConfig.enabled); dois dias depois,e6992b0c6desliga o card da home via config, mantendo as superfícies de nav e sidebar.f121f63a6instrumenta todos os CTAs comdata-pendo-id. No modal de ranking do MatchX, o título "Ranking Geral" foi movido pro header e revertido no mesmo dia (0d88ddac1,4cfbabc79,a71b27496).
Analytics e tracking
- Pendo com tagging estruturado (
3341fdd37):usePendoanexa ouser.idnumérico como metadatavisitor.user_idquando autenticado; opendoentra na matriz de destinos do registry de analytics com adapter pros eventos de conversãosign_up,withdrawedeposit_confirmed(ologinsegue só pelousePendopra não dupla-contar); oCtaButtonpassa a exigirdata-element-idexplícito em todo CTA, sem fallback implícito pro id do slot, pra o vocabulário ficar intencional; e navbar, sidebar, home e hub de cassino são tagueados com vocabulário neutro prefixado por área. Complementos:5d08445daderiva os ids de card de jogo e quick-access de fonte estável e8bdcc327ftagueia as setas dos carrosséis. - Container GTM secundário do 7k revertido (
80044def8) — encerra a linha aberta na semana anterior.583ce5988habilita Pendo nabetpontobet-bet-br. - AppsFlyer da
vera-bet-brmigrado pro proxy multibrand (68860b97c): os2sUrlsai do endpoint dedicado da brand pro proxy multibrand e entra o campo novoAppsFlyerConfig.brandSlug. Quando ele está setado, a rotaapp/routes/api/tracking/appsflyer.tsinjeta quatro headers derivados no servidor —x-brand,x-api-key(deAPPSFLYER_API_KEY),x-idempotency-key(UUID v4 por chamada) ex-request-timestamp— e devolve 503 quando a chave falta, falhando alto em vez de deixar escapar chamada não autenticada. Brands sembrandSlugseguem no contrato payload-only, sem mudança na superfície do client.
Inbox, busca e conteúdo
- Notificações do inbox renderizam HTML sanitizado —
7b447039acria a flaginboxRenderHtml,b5e43b86frenderiza HTML sanitizado nos previews eb93006781trata o título HTML vindo do Smartico, removendo<style>e<script>embutidos. - Jornada de busca inline-enter estendida a todas as brands (
85b0560db): o trigger global não navega mais no foco nem enquanto se digita — a digitação preenche um buffer local e só o Enter (com 2+ caracteres) semeia a store e navega pra/buscar, onde os resultados de cassino renderizam em grade cheia (perPage 60). Estava atrás de uma flagsearchJourneyexclusiva da donald; como override defeatures.tsé substituição de arquivo e não deep-merge, um default no base não alcançaria brands com override próprio, então a flag foi removida e o comportamento virou universal. - Busca deixa de pesquisar termo velho (
108fd101d): na montagem de/buscar, o efeitolastResolvedTerm → URLrodava uma vez com um termo reidratado dosessionStorage; ecoar esse termo velho pra?q=— combinado com o efeito URL→store consumindo prematuramente o guard booleano de update interno — disparavadoSearch(termoVelho)e sobrescrevia a busca que o usuário pediu ("digitei X, veio Y", que parecia cache no ambiente publicado).useSearchParamSyncpassa a pular o mount run euseSearchTriggerHandlerschamaclear()antes desetTerm()no Enter. - Detalhe de post do WordPress degrada em vez de derrubar a página (
defefc196): os três fetchers decontents.server.tschamavamres.json()fora do.catch, que só cobre o setup da conexão — se o WP responde 200 e derruba a conexão no meio do body, ores.json()rejeita e propaga até o loader, virando 500 no worker. Amplificador:buildPostDetailchamavafetchAllAreaPosts, que paginava a área inteira (até 50 fetches sequenciais) só pra montar um rail de 8 itens — 50 vezes mais chance de uma leitura cair. As três leituras de body foram guardadas comtry/catch+Array.isArray, então falha vira lista vazia: o rail desaparece e a página carrega.
Esportes
- Iframe da First descarregado no unmount pra parar OOM (
8d138966d): a First roda uma SPA pesada de odds ao vivo em iframe cross-origin. Sair de/esportesapenas desanexava o iframe, e a reclamação assíncrona do frame pelo WebKit ficava atrás do próximo mount, então alternar Esportes ↔ Cassino empilhava documentos pesados até o processo WebContent bater o limite de memória do iOS e ser morto — aba branca ou recarregada, reportada como "500". Confirmado por crash report de simulador (1,1 GB de WebKit Malloc). OSportsIframeFirstagora navega o iframe praabout:blankno unmount via ref persistida, liberando heap, DOM, websockets e timeline de animação de forma determinística.c095f8e18mantém o iframe em altura cheia quando o teclado do iOS empurra o viewport. - Sportsbook
rogueportado pramain-fix(07de21ea7): delta de 41 arquivos vindo da linhagemmain-no-cache-sports, aplicado 3-way sem nada do experimento "sem cache" — provider nativorogue(SportsRogue.tsx,rogue.server.ts, rotas/api/sports/rogue-*),@cactus-agents/sportsde^0.5.0pra^0.6.0,useSdkChromeOffsets,useSportsConfig, refactor desports-seoe overrides debetpontobetevera. - Proxy SSE da rogue streamado chunk a chunk (
3aae4f9cb): o browser fecha o SSE constantemente (reconexão, navegação, troca de seleção no betslip) e o upstream dropa conexão idle; sem tratamento, o undici lançavaTypeError: terminatedcomo rejeição não tratada. O body upstream é re-streamado manualmente, engolindo o abort como fim de stream esperado, e o fetch upstream é amarrado aorequest.signalpra desconexão de client abortar upstream (499).49fc4b972adiciona deep-links de sidebar da rogue parapb-bet.
Gamificação e layout
- Contador de tempo restante volta nas missões (
69728b90c): contadorHH:MM:SS(com prefixo de dias quando ≥24h) em missões ativas com prazo futuro, no card e no modal — port do contador do legado Vue, perdido na migração. Reusa ouseCountdownextraído detournament-internalspraapp/hooks/useCountdown.ts(o módulo antigo reexporta, importadores intactos), comMissionCountdownBadgee o helper puroshouldShowMissionCountdown. Cor via tokentext-primary. Gateado porgamificationMissionCountdown(default off, ligado em 7k, cassino e vera). - Descrição estruturada de torneio recuperada de JSON inválido (
a3efbfcea): o CRM enviadescriptioncomo objeto JSON digitado à mão, com aspas duplas internas não escapadas no texto de termos, o que faz oJSON.parseestrito lançar e derrubar o payload estruturado inteiro. Entra um fallback ancorado em chave que extrai cada campo preservando aspas internas e newlines verbatim, com teste de regressão.2263cb7f1e1a0c89855completam o parser no lado da donald. - Scroll-lock de fundo opt-in enquanto overlay está aberto (
9470aab36), gateado porlockBackgroundScrollOnOverlays(defaultfalse, só a donald liga).edb9fe38erestringe o drag a header em sheets no modo snap, pra clique em botão não ser engolido. - Label discreto de versão de build no footer (
fa7619da2): mostraVersão: {BUILD_ID}(hash de 12 caracteres, de~/utils/build-id) nos dois footers, pra dar visibilidade da versão em produção pra suporte e debug, viat("footer.version", { defaultValue: … })— tradução pelo core depois, sem exigir release agora.
Core / SDK
@cactus-agents/payments(add8870): resolução de variação de método por match de prefixo mais longo, consumido no base por006925d74.@cactus-agents/payments+accounts+i18n(db5eb6f): statuses de pagamento v3 em inglês e método de wallet-redirect — o backend migrou de PT pra EN nessa semana.
Brand Overrides
- donald-bet-br segue como o maior volume da semana. Ajustes de brand em
cc5ba58e8(fonte Inter, remoção de fade, itens de menu, header in-game). Footer: full-width abaixo da row com cap de altura da sidebar (b0ddbd8aa), remoção de copyright (f38cd661c) e da linha "Feito com ❤" (556621df9), links de crash e slots repontados (f3e32f367) e o link de Ouvidoria reposicionado e em caixa alta (173d8c4f6,d6849b832,51b2f6ac5). Header: badge de notificação do avatar deixa de ser cortado (8102360ec,e9a0d396d). O drawer de pré-jogo ganha launch em tela cheia in-place (ba04ba03d), fechando o fluxo aberto na semana anterior. Recentes: card com dica "+" (2c4063f8b), largura casando com os carrosséis de categoria (5989b35f6), label "Jogos recentes" (4455776c8) e row oculta quando vazia (661b0a6df). Também recompensas com valor entre chaves (c181964cd,43f6ce162), "Participando" como botão de sucesso quando inscrito (612d24c71), menu do usuário em duas colunas no desktop (4f69c1867), link "Prêmios" com badge de contagem (d35ac2c1d), termo de privacidade estático (aba1d579a), chips de provedor redimensionados (7fa5eb78c), escala de input no iOS (5471030ec), fallbacks de i18n de auth movidos pro override (9b0e06450), Top 10 no slug derecommended-banners(27ac69f03) com "ver mais" apontando pra todos os jogos (a7cbfe937), grade sincronizando ao trocar categoria (d2eebe07d), "Pagou" em janela de 24h ocultando dados nulos (fee4febd8) e mensagem VW003 do saque direcionando pra Dados da conta (e8fba8bf8). - Seções showcase reconstruídas a partir de categorias reais (
11d3a3d3a): "Novidades" passa a sair da categoria filtrávelnovidades, porque o slug curadojogos-novosnão é filtrável no BFF e voltava vazio, e "Mais jogados" passa a usargetByCategorypaginado no client.GameGridganhamaxRowseGameCarousel/GameSectionganhamitemWidthClassName, usados só quando a brand opta. - Perfis oficiais desativados em 7k, cassino e vera (
25eb139cf):featuresConfig.profiles.enabledvai prafalse, e o gate único emgetActiveProfilesConfig()desliga a superfície inteira sem deletar código — as rotas retornam 404, o widget de sidebar e a row da home se auto-escondem e as entradas saem do sitemap. - 7k-bet-br: banner de instalação do app removido das notificações inferiores (
503588285) e restaurado no dia seguinte (fa3b2b922);orderByde slots removido pra usar a ordenação default da API (7d06af9a4); pill de saldo no modal de check-in diário (c29bd9ad2); histórico de login e aposta apontando pra/historicocom filtro (7104bd3bd); identificação do usuário logado no chat do Zendesk (d4235ad1d).
Pagamentos
client_request_idúnico por geração de depósito (f9652af1e): oDepositModalgerava a chave de idempotência v3 emhandleSubmitDeposit, antes do desvio do Kushki, e só a resetava no fechamento do modal e em "novo QR" — nunca em falha de submit nem em troca de provedor. Depois de erro → trocar provedor → retry, o mesmoclient_request_idsaía com payload diferente, gerando conflito de idempotência no BFF (o incidente "repetiu o ID"). A chave passa a ser gerada dentro desubmitDepositRequest, só quando uma charge é realmente criada, e é resetada em falha, espelhando o padrão doWithdrawModal.- Método do Hub encaminhado como veio (
24415463c): o front normalizavahub_payment_methods.methodpra maiúsculas, o que quebraria valores comocredit-card. O Hub é a fonte de verdade, então o valor exato é repassado. - Statuses v3 em inglês exibidos, retrocompatível (
4ffb68688):WithdrawSuccessaceitaAUTHORIZEDeAWAITING_APPROVALao lado dos valores PT, case-insensitive;TransactionCardganha badges e chaves de i18n pros statuses novos de falha (failed,integration_failure,provider_rejected) expostos pelo@cactus-agents/accounts. Bumps:payments ^0.21.0,accounts ^2.7.0,i18n ^0.94.0. - Depósito Pix em um clique via
?open=pix(63851584c): o contrato?open=pix&value=<reais>abre o modal pré-preenchido e auto-submete um depósito Pix, levando direto à tela de QR Code. Sobrevive ao round-trip não-autenticado → login/registro → autenticado e cai pra formulário pré-preenchido quando não há método Pix, quando falta registro de documento ou quando o valor é inválido. Gateado pordepositPixQueryParam, só nadonald-bet-br. - Módulos de validação não suportados descartados pra destravar o saque (
9761e9dad): o backoffice da donald configurou um módulocontactemvalidation_modulesque o front não sabe renderizar. Num contextofrequent(saque), esse módulo desconhecido é forçado aisValid: falsea cada avaliação e, sem UI pra limpá-lo, trava o saque pra sempre. A flag opt-indropUnsupportedValidationModulesfiltra os módulos por allowlist antes da avaliação — filtrar a config, e não a decisão já calculada, faz o core recalcularallowedependingModulecorretamente. Ligada só no override da donald.
Infra / CI/CD
- Namespace do middleware amarrada ao
CACHE_GENERATION(136cba2fa): ogetBuildId()produz a chave usada noscaches.open()do próprio worker (assets:*,api:*,ssr:*,ssr-auth:*), e o prefixo era hardcoded da era rasj — bumpar oCACHE_GENERATIONnão afetava essas quatro namespaces, deixando-as órfãs na geração antiga. Agora lêenv.CACHE_GENERATIONcom fallback pro default canônico, restaurando a garantia de rotação fleet-wide.008a9a79ealinha oCACHE_NAMEdosw.jsao mesmo token. - Purge pós-deploy pulado nas brands
.bet.brde produção (ops84eec36): a regulação brasileira exige que as zonas.bet.brapliquem geo-block de edge restringindo tráfego a IPs BR. O step de purge roda de runner do GitHub Actions hospedado nos EUA, então o edge devolve 403 "Region Unavailable" antes da request chegar ao Worker — e o header de bypass de WAF não ajuda, porque o geo-block roda numa camada anterior. Confirmado empiricamente navera-bet-br.e07455efaz o mesmo nafi-7k-bet, por Cloudflare Access. - Dropdowns do Manual Cache Purge auto-sincronizados (ops
827f48b+97672bd):environmentdeixa de ser texto livre e vira dropdown com as 32 brands lidas deconfig/brands/web-base/environments/, e entra o inputtag_quickpickcom as 30 tags declaradas emconfig/cache/*.yml. Como YAML de workflow não lê filesystem no render doworkflow_dispatch, um script novo regenera as listas automaticamente, fechando o loop de drift.af80834corrige obump-cache-generation, que dispatchavadeploy.ymlnofront-web-basequando o workflow real se chamaci-deploy.yml. - TTLs do service-api retunados (ops
0df3849): endpoints de 1h caem pra 30min,/v2/bff/featurese/v2/bookmaker-settingspra 2min e/v2/casino-game-votespra 10min; o cap de 1h fica.front-service-api66de43ereconcilia o fallback hardcoded, que estava defasado em relação ao YAML — o fallback só serve se a injeção deSERVICE_API_CACHE_POLICY_JSONfalhar. - Duas rodadas de bump de geração (ops
e1fbd01apcsrasj-v3→v4, motivo "Categorias Donald", e4b86ff4v4→v5). fecadmfecha o uplift da semana anterior —0ccecf9passa a rodar build antes do typecheck, porquecliewebimportam a saída de build de@fecadm/core, ea7153a4resolve as falhas de CI restantes e o lint.- Avaliação de zero-cache — vários environments foram temporariamente apontados pra
main-no-cache(74720bf,0099bcb,e19a840,82e594b,eafe257,a38b899) e depois revertidos (2b28f3b,ca9fae3). No mesmo período,80a92a8liga auto-deploy dadonald-bet-bref4ca676/d2a843emovem abetpontobet-bet-brpramain-fixe de volta.