Quarto provider de sportsbook, e o primeiro renderizado in-app — 4c37f8967 integra o rogue: um sportsbook React nativo entregue pelo SDK @cactus-agents/sports-rogue, montado dentro da própria aplicação em Shadow DOM e consumindo a Rogue API da First. Diferente de First, Altenar e Betby, que são iframes, aqui o sportsbook é componente. O SportsRogue.tsx monta o SDK via createSportsbook, registra callbacks de auth / login / depósito / share, sincroniza histórico (pushState + popstate → instance.navigate), restaura share links via ?betslip= e aplica offsets de layout por useSdkChromeOffsets.
Rotas server-side e segredo que não chega ao browser — o commit adiciona rogue-anonymous e rogue-login (token) mais rogue-proxy/*, um proxy same-origin pro domínio do provider com streaming de SSE, resolvendo CORS sem expor nada ao client. A chave de API é server-to-server: entra via extraHeaders do api-client e é injetada nos endpoints de token a partir de secret, nunca chegando ao browser. O app/config/sports/provider.ts ganha FORCE_SPORTBOOK (resolveForceSportbook / applyForceSportbook) pra ativar o provider por ambiente sem mexer no main estático da marca. O módulo é autocontido: escudos e assets vêm do CDN default do SDK, sem plugin de cópia no host.
Suporte no core, sem vazar tipos do SDK — 170ca69 declara "rogue" como SportsProvider reconhecido em @cactus-agents/sports, adiciona RogueProviderConfig (tema / scheme / estilo de odds / cores, mais passthrough), roguePath opcional em SportsSidebarItemBase e o caso correspondente em resolveMenuItemPath. Em @cactus-agents/api-client entra extraHeaders opcional em CactusServerClientOptions, mesclado nas requisições de saída, permitindo injetar segredos server-to-server sem forkar o client. Tudo aditivo: comportamento default inalterado. A config do rogue usa superfície comum tipada mais passthrough justamente pra que o SDK siga como fonte de verdade dos próprios tipos.
@cactus-agents/sports-rogue não vive no front-cactus-core — vale registrar explicitamente: o pacote é uma dependência @cactus-agents/* real do front-web-base, mas não está em front-cactus-core/packages/; é publicado de outro lugar. Hoje o PROVIDERS do base declara quatro providers: ["first", "altenar", "betby", "rogue"].
Auth do SDK reconciliada depois do init — 503560f37. O SDK ficava preso em anônimo mesmo com o usuário logado: o efeito de isAuthenticated chamava instance.setAuthenticated() antes de a instância existir (durante o import dinâmico do SDK), virava no-op, e como isAuthenticated não mudava de novo nunca havia nova tentativa — só rogue-anonymous era chamado, nunca rogue-login. O fix guarda o isAuthenticated num ref e, depois do await instance.init(), chama setAuthenticated(isAuthRef.current) pra reconciliar qualquer flip ocorrido na janela de import/init.
Trem de bumps do SDK na mesma semana — fdada3743 (^0.4.0, criador de apostas), 570d71af2 (^0.5.0), a78417352 (^0.6.0), c95dc81f8 (^0.7.0) e 9721f814f (^0.8.0). O 0.3.0 trouxe auth lazy, dedup do token de SSE de 4+ chamadas pra 1 e retry na sidebar de esportes. O pacote segue evoluindo rápido — hoje o base está em ^0.29.2.
Álbum de figurinhas: ranking, incidente e rollback
Ranking e badge entram — 724b2f964 adiciona a seção de ranking na aba Missões com indicador de loading, 62856a52f passa a suportar o shape flat do BFF mais a posição do próprio usuário na lista, e 5dc85050f liga o badge de figurinhas disponíveis no banner, no título e na sidebar de uma marca.
A regressão — a chamada opcional de ranking usava o fetch com retry, cujo retry de 401 invalida o cache de token compartilhado e refaz o handshake, o que pode rotacionar o token e derrubar as chamadas críticas paralelas do bootstrap, fazendo loadAlbumPageData retornar null — o álbum caía no gate de "escolha sua trilha". a3168fa8f troca pro fetch sem retry (o ranking passa a falhar em silêncio sem afetar o burst), 66dec83a3 adiciona single-flight no handshake de auth, 6501911a3 remove a chamada de progresso no login, 9e7e3a2db e e95077a18 garantem que /api/album/*nunca retorne 401 (era essa a origem dos logouts forçados) e 6838c3127 tira o ranking do burst de bootstrap.
O rollback — 1cbba2ca8 reverte oito commits do álbum que entraram em stage entre 08 e 09/06 pra investigar a regressão observada em produção: 734 mil logouts forçados em 30 minutos mais bootstrap nulo recorrente. Cinco dos fixes acima existiam apenas em stage; o revert junta todos pra deixar o álbum no mesmo estado pré-rajada, em paridade com o revert que foi pra main. 2ffdeb97c completa o rollback junto com o CI de esportes, também pra paridade. Fica registrado como incidente real: uma chamada opcional de ranking, ao compartilhar o cache de token com o caminho crítico, conseguiu deslogar a base.
27dbfee0e e a93adae16 aumentam a área clicável do X / voltar no header do álbum.
Modal de ranking de sweepstakes — 26c9b2b52 reativa o ponto de acesso no agregador flutuante como "Copa dos Palpites": em vez de deep link direto, o clique abre um modal com a logo da campanha, o ranking ao vivo dos 20 primeiros e um CTA. Entra o service mais as rotas de proxy /api/sweepstakes/{game,top-players,player} consumindo a API externa por header de brand; o MatchXRanking / MatchXRankingModal traz pódio dos top 3, lista de 4 a 20 com scroll e indicador fixo de posição com animação de entrada e saída por IntersectionObserver; o CTA tem gate de auth (deslogado abre login) e deep link dinâmico por matchId. Entram também o discriminante modalAction em sidebar-buttons e um slot próprio no CtaButton, mais duas envs por marca.
Gate por feature flag — 71c3c2fac põe o ponto de acesso atrás de uma flag no mesmo padrão do álbum (habilitado em build-time e keys remotas legadas/ConfigCat em AND), ativa só em uma marca e closed-by-default quando a key remota não existe ou é falsa. O mesmo commit corrige o indicador de "sua posição", que aparecia sem dados reais porque um placeholder vazio (name: "", points: 0) não resolvia pra null.
00d4b7af7 dá layout empilhado ao modal quando não há ranking e 56a9b7dc7 reaproveita o slot de CTA existente em vez de criar um dedicado.
Orquestrador headless para SDKs que pintam o próprio modal — 0db39db8e. O SDK do Unico com displayMode: 'modal' é um "self-modal": desenha o próprio frame em z-index 2147483646 e toma a viewport de propósito. Hospedá-lo dentro do KycModal produzia duas superfícies de modal competindo na mesma área — visualmente quebrado (painel de verificação vazio aparecendo atrás ou embaixo do modal do Unico) e conceitualmente errado. A arquitetura passa a reconhecer dois padrões de ciclo de vida: in-modal (operadores de iframe e o WebSDK do Sumsub, que renderizam dentro do container do KycModal) e self-modal (Unico, que provê a própria superfície e torna o nosso modal redundante). Operadores self-modal agora montam no nível do app shell via o KycSdkOrchestrator headless, desacoplando o ciclo de vida deles do estado de mount de qualquer modal; o KycModal retorna null cedo nesse caso.
Migração pro pacote oficial — core 3ab2638. O launcher anterior carregava o script do Unico à mão e chamava uma API privada do SDK, que não tem displayMode nem onWidgetVisibilityChange — resultado: o SDK renderizava um backdrop fixo em z-index 2147483646 que sequestrava eventos de ponteiro de toda a UI do host (modais incluídos), deixando o painel de verificação visível mas não clicável. A reescrita passa a usar o pacote npm oficial idpay-b2b-sdk, espelhando o operador legado de segunda geração: init com displayMode: 'modal' pra o SDK gerenciar a própria camada corretamente, onWidgetVisibilityChange exposto pro host reagir quando o usuário dispensa o modal sem terminar, e um tipo de evento de finalização exportado pra o caller discriminar FINISH de CLOSE / ERROR.
3f4271944 derruba o interceptor de iframe e passa a confiar na camada de modal do SDK oficial, fb80b02e4 quebra o loop de launch e suaviza o flash de init, e bb6afba47 dispensa o loader logo após a resposta do BFF pra que os painéis de Unico e Sumsub fiquem interativos.
Torneios recorrentes colapsados em um card por template — 69ce05d31. O Smartico devolve uma linha por instância agendada, então uma campanha recorrente (diária ou semanal) renderizava como vários cards compartilhando o mesmo tournament_id. O dedupeTournamentsByTemplate de @cactus-agents/gamification passa a ser aplicado por aba de status na listagem e no carrossel da home/hub: a aba ativa mostra a rodada ao vivo uma única vez, enquanto as abas de "novos" e "finalizados" seguem expondo as rodadas futuras e passadas. O helper de core (4aa10de) escolhe o representante por ranking de recência — em andamento → próximo mais cedo → ativo/aberto → finalizado mais recente → cancelado — preservando a ordem de primeira aparição pra o caller manter controle da ordenação final.
Opt-in de push migra pra topbar com cadência de 12h — 14c5962ba (com o gêmeo 1f8817ba6). Em três marcas, o push_notify sai do card flutuante de baixo e vai pra topbar (structure.topbarNotification: true), com recorrência de 12h via cookie e shouldShow restrito à permissão default, ou seja, para de importunar quem já bloqueou. Entra também a flag requestPushOnLogin e o maybeRequestPushOnLogin() no LoginModal, em best-effort: o popup nativo só dispara no Chrome, porque Firefox e Safari exigem gesto do usuário.
Link de download resolve loja ou landing page conforme o SO — 019027654. Sidebar e rodapé passam a resolver o destino de "Baixar App" pelo href do useAppInstall em modo read-only, sem chamar handleInstall: Android e iOS com loja disponível viram link externo em nova aba, desktop ou ausência de loja levam pra /lp/download-app na mesma aba. Os três estilos visuais de sidebar trocaram button onClick por componentes de link mantendo o closeMobileMenu, e o rodapé trocou to: "" com handler por to: downloadHref com flag de externo.
Item escondido no desktop — ebfa56a9f adiciona hideOnDesktop ao AppInstallConfig: quando ligado, o link de "Baixar App" na sidebar e no rodapé aparece só em iOS/Android, onde existe href de loja, e desaparece por completo no desktop. Ligado em três marcas; as outras não declaram a flag e seguem inalteradas. Topbar, widget flutuante e badge não são afetados.
2a86fded8 e c5ae1f08d fazem o CTA renderizar como <a> quando existe href de loja, em vez de botão.
Favicons com path relativo — d0046b8e8. As tags <link> de favicon eram emitidas com URL absoluta montada de https://${ORIGIN_DOMAIN}, o que cravava o domínio de produção no markup de todo ambiente. Em dev, stage e qualquer preview o browser buscava esses ícones em produção. O sintoma apareceu no onboarding da marca nova: o stack novo estava no ar em stage, os arquivos de override em overrides/<brand>/public/ estavam corretos, mas o ambiente publicado nunca mostrava os ícones da marca — cada request de favicon era respondido pelo domínio de produção, que ainda servia o stack legado, com 404 em /favicon.ico e em /pwa/*.png. Nenhum ambiente exibia o ícone certo.
Sitemaps retunados conforme a planilha da consultoria de SEO — 0aec0944c (vera) muda o blog.parentSlug de geral pra blog, destravando ~90 posts que estavam ocultos por escopo limitado à subcategoria, e aplica priority por post via regras exatas mantendo os wildcards como fallback, mais priority/changefreq de 8 categorias de cassino. 871ba29f1 faz o equivalente na cassino e 0a67c4eb6 adiciona sitelinks internos.
Blog passa a ter uma fonte única de sitemap — 9324d8d41 remove o grupo sitemap-blog.xml, que duplicava cada post, e move as priorities de blog pro routeOverrides, que alimenta o content-sitemap /sitemap/blog/<cat>.xml — a fonte que a consultoria valida. Antes o mesmo post aparecia nos dois lugares com valores diferentes (o grupo com o valor correto, o content com 0.5/weekly por falta de regra), gerando inconsistência. Sem wildcard /blog/*, pra não rebaixar as páginas de listagem de categoria.
Ligas do 7k reordenadas — 839f7778e (com 9c3ec6c48) define a nova ordenação das 12 ligas populares no menu lateral e espelha as mesmas 12 no topo de "Principais Ligas" da home, mantendo as demais ao final e sem duplicatas. Entra o override overrides/7k-bet-br/app/config/layout/sidebar-sports.ts com popularChampionships reordenado, mais 10 ícones custom horizontais de liga e 3 de sidebar; o useSidebarSections passa a priorizar item.icon, depois a flag, depois o ícone de esporte.
Recuperação por limite de tentativas na betpontobet exige KYC primeiro — 616b0b89e (com 8a688d1f4). No bloqueio por 3 tentativas a marca exige o KYC antes de qualquer coisa: o getOptions dava 404 quando chamado antes do KYC, e o /confirm exige um code de OTP, nunca aceitando kyc_id nesse cenário — paridade com o legado. O fluxo novo (só nessa marca, atrás da flag opcional recoveryAttemptLimitDirectKyc) vira attempt-limit → KYC como gate → getOptions(recovery_token) → opções de e-mail/SMS → OTP → confirm sem kyc_id. Com a flag desligada, o fluxo original segue intacto — mudança puramente aditiva.
6292631fb preserva atualizações otimistas de perfil entre navegações e 7e15d2468 liga forceRegisterValidatePhone na cl-bet7k-com.
Contact card com botão de suporte ao vivo — 703275b4e adiciona a flag helpCenterContactLinkAsLiveSupport (default false). Quando ligada e a marca tem live support configurado, cada contact card em /ajuda que tem entry.link substitui a linha de site externo por um botão "Suporte ao Vivo → Iniciar Chat" que abre o widget nativo via openSupportChat(), em paridade com o link do rodapé. Sem live support configurado a substituição é no-op e o link externo continua como fallback. Ativa só em uma marca.
Pacote @cactus-agents/kyc migra o launcher do Unico pro pacote npm oficial idpay-b2b-sdk (3ab2638), com onWidgetVisibilityChange e evento de finalização discriminável.
Pacote @cactus-agents/i18n recebe 25ffd61: o título do formulário do gateway de cartão usado no Chile passa a dizer "cartão de crédito/débito" nas sete locales. O gateway aceita as duas modalidades, mas o modal de depósito renderizava só "cartão de crédito", induzindo o usuário a achar que débito não era suportado. Visível hoje só na marca chilena, que é a única cabeada nesse gateway; as outras locales foram atualizadas por consistência.
Integridades do lockfile regeneradas pro registry EMU — 3a92c1b88. Os pacotes foram republicados na organização gerenciada nova com hashes diferentes, o que quebrava instalação com lockfile congelado (409). Sem isso, nenhum install determinístico passava.
donald-bet-br: semana quase inteira dedicada ao design system da marca nova, ~70 commits. Tema (75f1dd075): cores, header e input de busca ganham knobs novos brand-overrideable (headerStyle, searchInputStyle mais tokens de field como CSS vars consumidos por SearchPill, HeaderSearchInput e SearchPageInput), logo apontando pro asset local do override, e bottom nav com knobs de chrome (fundo translúcido, blur, cantos arredondados, sem borda), ícones custom, item Home no lugar de sports-live, ordem própria e tile ativo branco. Header e auth (ce9ce141e botões Entrar outlined + Cadastrar preenchido, fa209d330 header centralizado com badge no avatar e ícones custom de sidebar). Rodapé — a maior sequência: fundo grafite (8972793a9), acordeão de navegação (b3f689393) com Esportes e Cassino virando grupos com sub-links (139e8c702, c11eae3d4), faixa de contato com e-mails abaixo dos selos (a91fe650d, com tipografia em a44ee9896 e padding em aa879bf80), macro layout de 2 colunas no desktop (4c1a2a552, com ouvidoria e copyright movidos pro fim da coluna direita em 711b3dc0a), faixa "Siga-nos" no topo do mobile (2389cf7a7), botão "Baixe nosso app" abaixo do acordeão (a043ca71c, tipografia em 0f7bdfa4d e ícone em 584756e9f), selos capados em 34px (c72074f6a) e a faixa legal boxada (5abdd8c60, 4295efe5c). Cassino — 08ebfcd49 adiciona o layout de grade única filtrável ao hub, gateado pela flag nova cassinoLandingLayout ("rows" | "grid", default rows, opt-in só nessa marca): "Pagou hoje" → busca → chips de categoria que filtram a grade no mesmo lugar → provedores → grade paginada com botão "Carregar mais jogos" → carrossel de novidades, servido pelo hook useCassinoGrid (categoria e busca server-side, append por offset/limit, reuso do SSR enquanto pristine) mais CassinoFilterChips, CassinoGridLayout e um loadMoreMode novo no GameGrid. Widgets de home — eeb7a6c5e (Top 10), 3eebac604 / 8df4fd33b ("Pagou hoje"), 343892be6 / f8175de6b / d253b7079 / d614cc80e (provedores como chips de logo branca, responsivos), e1c3848e9 (card de torneio overlay), 2a2001052 (row de jogados recentemente, escondida quando vazia por 6dfcbda36), d014a46af (SectionTitle com "Ver mais"), 9559297ce ("Jogos Novos" → "Novidades" com ícone custom). Card de jogo — 1ff465ea7 / 8d1097248 adotam um variant de card já existente (thumb com borda soft-light permanente, overlay com blur em hover e pill JOGAR com sombra pressionada, chip de medalha flutuante), e 4f88e6299 / 29b957181 / 5611f3e3d / 0513c0547 convergem pro variant "cover" (só imagem) com aspecto e radius responsivos. ac517ffb7 fecha a semana redesenhando a sidebar desktop como painel flutuante, e e7d80d7f4 garante o fundo grafite do rodapé sobre CSS injetado por tag manager.
7k-bet-br: ligas de esportes reordenadas; badge de figurinhas do álbum (revertido); Copa dos Palpites ativa; agregador flutuante com imagem de FAB atualizada (abd8e9591), ícone de CDN pra um dos itens (a0a39a328) e um item posto em standby (fb92c3026); a9c1beb1e / f88bb5b0a corrigem o speed-dial fechado, que engolia cliques.
betpontobet-bet-br: recuperação por limite de tentativas com KYC como gate; contact card com botão de suporte ao vivo.
vera-bet-br e cassino-bet-br: sitemaps retunados conforme a planilha da consultoria de SEO; blog com fonte única de sitemap.
Branch de deploy resolvida pela config do front-ops, não por input — f822c32. O inputs.ref sai dos dois workflows reusáveis e a branch deployada/auditada passa a ser resolvida inteiramente pela config de ambiente deste repositório: front-ops é a única fonte de verdade, sem override humano em lugar algum. Em workflow_dispatch, o SHA de HEAD da default_branch do ambiente é resolvido via API do GitHub, e a branch selecionada em "Use workflow from" na UI é intencionalmente ignorada; em push, segue usando o SHA já validado contra a default_branch a montante. A default_branch passa a ser output do job de check e ganha uma linha "Source branch" no resumo do deploy. 613940201 já tinha pinado a branch do dispatch manual na default_branch do front-ops.
Ambiente e branch de esportes no CI — 633a6c0b5 / 508655e69 adicionam a branch de sports e o ambiente correspondente ao ci-deploy, com 5981e2c3f / ac12b5032 cobrindo o drift do config-lint e 74ff4add5 readicionando o que se perdeu numa resolução de merge. Do lado do front-ops, 7899ad1 cria a configuração do ambiente novo.
Auto-delete de branch no merge de PR, em todos os repositórios — base 8710ca165, core 64c2695, front-ops 88b7971, front-tests-e2e 169e6fb, front-service-api 95287a0, front-service-api-dev-proxy ce569e3 e fecadm 8658275. A branch de head é deletada no merge, com exceção de develop → main.
CODEOWNERS migrado pros usernames da organização gerenciada — d9fe90366. Pós-migração EMU, os owners nominais de /.github/ (adicionados em 27/05) ainda apontavam pros handles antigos, desativados sob o enterprise gerenciado — o review de CODEOWNERS não resolvia pra conta ativa nenhuma, então a proteção de branch que exige aprovação dos owners ficava inaplicável.
front-ops: default branch da cl-bet7k-com alterada (5feaebc).