BREAKING: cassino-bet-br e vera-bet-br saem do front-web-base
Janela desta entrada — cobre 27/07 a 02/08 (60 commits em main-7k). Duas mudanças breaking e duas features apagadas por completo: é a semana mais consequente do trimestre pra quem consome o template.
As duas brands foram removidas do base e vivem hoje no fork front-web-external (PR #1835) — 84057c160 (refactor(brands)!) apaga overrides/cassino-bet-br e overrides/vera-bet-br inteiros. O diretório de overrides do base vai de 15 para 13 brands rastreadas: 7k-bet-br, betpontobet-bet-br, casateste-com, cl-bet7k-com, donald-bet-br, fi-7k-bet, ng-7k-bet, pb-bet, ph-state77-com, pt-state77-com, rj-bet, state77-com, x2b-bet. O commit toca 439 arquivos e remove ~28,4 mil linhas.
Para onde elas foram — ops bd6d1ee cria config/brands/web-external/brand.yml apontando pro repositório cactus-agents/front-web-external e MOVE 5 environments de web-base pra web-external: cassino-bet-br, vera-bet-br (com default_branch de main-external pra main), stage-cassino, stage-vera e sports-vera. O placeholder config/brands/vera-bet-br é removido — um fork atende as duas. Ops e01a693 já havia apontado os envs pra main-external em 18/07, e ops 3b555c3 ajusta o required_team de deploy protection dos dois. No base, 2d47d394d limpa o dropdown de workflow_dispatch.
O que morreu junto, por não ter mais consumidor — o commit é a melhor documentação do próprio efeito colateral: saem ~40 variantes órfãs de layout e widget (header-split-bar, header-vera, sidebar-sectioned-titled, sidebar-wide, header-secondary-flat, as bottom-navs illustrated-tabs/grid-tiles/betano/fab-center, TopGameCardLegacyGrid/CompactWatermark/Outline, SectionTitleNeon/Vera/Panel, CtaButtonVera, BiggestWinsListWidget com HomeWinsRightColumn, RecommendedGamesCarousel, ProfileCompactHero/CenteredSpotlight, FooterBadges) e features exclusivas das duas (ReferralIndicatorV2 com suas rotas de API, todo o Incognia, useAutoLogoutEndpoint com a rota /logout-auto, gamificationRelevanceSort, homeRightPane, usePaginatedGames, useRemarketingId). Mantidos de propósito por decisão de produto: ftd*, profiles, social auth, os módulos VIP, sports-rogue e RecommendedGamesBento (default do base).
Consequência prática pra leitores do changelog — todas as entradas anteriores que tratam cassino-bet-br e vera-bet-br como brands do base descrevem um estado que não existe mais aqui. Mudanças nessas duas brands a partir de 28/07 acontecem no front-web-external, cujo histórico não é visível deste workspace.
Zero jogos do BFF no doc request (PR #1839, especificação 2026-07-28-spa-shell-listagens-design.md em docs/superpowers/specs, que é a fonte canônica pro comportamento novo, inclusive pro trade-off de SEO) — 2e130e204 (feat(casino)!) transforma os loaders de /cassino, /cassino/ao-vivo, categoria e provedor em shell-only (só meta e um getBase leve); grade e rows chegam pelo client via useGamesListing e useHubRows, e cada filtro passa a ser uma request visível no Network. useHybridGamesList e filterLocal foram REMOVIDOS: o caminho passa a ser único e server, porque o modo local em memória não sabia responder categoria custom.
O que a nota de breaking diz explicitamente — rows e grade saem do HTML do doc request, então nomes de jogos deixam de ser indexáveis nessas páginas (title, meta, canonical e JSON-LD de head continuam SSR; o trade-off de SEO foi aceito como decisão de produto). E personalize orderBy por stats passa a valer por página carregada, não globalmente. Exceções que mantêm SSR próprio: donald (grid) e o fluxo refer-friend.
Os hooks — 20316364a: useGamesListing faz a grade sempre server-side via /api/games/* (fetch no mount, infinite scroll por offset, term com debounce, filtros na query, facets da query base congelados, erro com retry, requestId contra respostas fora de ordem e enabled=false pros fluxos com lista SSR própria); useHubRows traz as rows dos hubs e semeia os hubChips na store.
A rota nova e o cache curto — 590ed3fba adiciona GET /api/games/rows/:page (cassino ou cassino_live) com 404 pra page inválida e cache de 60 s, faz /api/games/{category,provider}/:slug devolverem os facets do BFF na MESMA resposta da lista, e baixa o cache de browser dessas rotas de 30 dias pra 5 min e 60 s — na casca elas são a ÚNICA fonte da grade, sem page-1 SSR fresca por baixo.
Chips contextuais e facets de disponibilidade — b55b29fdd: os hubs derivam os chips das rows da própria página (deriveCategoryChipsFromRows), a página de categoria ganha contexto de hub pai via ?hub= validado com inferência e fallback pro catálogo curado (resolveHubContext, deferido no loader), o seed otimista por hub em useGamesStore mata o flash de catálogo nas transições, chips indisponíveis ficam opacos e vão pro fim da fila em partição estável, e categoria e provedor migram do agregado deprecated (getByCategory/getByProvider) pro getListPage.
Requisito de core — f5e5163b1 traz @cactus-agents/games 2.2.0 (core 68ec4ad), que expõe os facets do GET /casino-games/list. Sem o pacote, o typecheck:ci da casca não passa.
~180 commits de trabalho apagados (PR #1834) — 52cfb2267 remove o módulo inteiro na main-7k, em paridade com a remoção já feita na main-external, e como superset (cobre também os extras que só existiam aqui). O commit toca 174 arquivos e remove ~29,5 mil linhas: componentes (app/components/album e album-host), rotas /vip/album e /album/figurinha/:slug, rotas /api/album/*, services (album.service.server, smartico-album.client/merge, adapt-campaign), a store albumProgress, os hooks useAlbumProgressSync e usePostLoginReturnTo, configs por brand com sticker-catalog, a entrada gamification.album nos paths de 15 brands mais o base, o namespace album nos 4 locales, o never-cache de /vip/album no Worker, as envs STICKER_API_* e o documento de auditoria do port v2.
O que foi preservado — slugify migrou pra app/utils/slugify.ts (usado por pendo-id, quick-access e sidebar) e o badge estático da sidebar foi refatorado pra badge.ts, porque o dynamicBadge de album-packs era o único dinâmico. rewardsCount e o badge do menu horizontal são sistema separado e ficam.
Arco honesto do trimestre — o álbum entrou no base em 11/05 e saiu em 28/07; o Bolão/MatchX entrou em 08/06 e saiu em 23/07. Duas features construídas e removidas dentro do mesmo trimestre.
Missão de redução de bundle (3 ondas) e correções de build
Micro-chunks consolidados (PR #1852) — 1b5d7e0fc migra rollupOptions.output.manualChunks (deprecated) pro output.codeSplitting.groups nativo do rolldown, só no build de client. Três mudanças: grupos com PRIORIDADE, com vendor-framework (100) reivindicando React antes de qualquer grupo lazy — o que elimina o mecanismo que duplicava o core do React dentro de chunks agrupados (incidente de 08/07); um grupo icons que junta os 277 ícones do unplugin-icons num chunk de 80 KB gzip em vez de 120 chunks de ~500 bytes; e um grupo app-shared (minShareCount 3, maxSize 200k). Medido na home 7k: requests de JS de 212 pra 46 (-78%), gzip eager praticamente flat (527 KB pra 530 KB) e total de chunks do build de 537 pra 307 (-43%). bf5f4d64e (PR #1853) adiciona strictExecutionOrder pra blindar ciclos de chunk.
HTML jurídico sai do bundle do client (onda 3/3) — 1fb262d78: o footer, presente em toda página, importava buildLegalLinks → legalPages → ~90 KB de HTML legal em 4 idiomas no grafo global do client. O novo app/config/legal/pages-content.server.ts isola o mapa slug para HTML (o sufixo .server exclui do client bundle no RR7), pages.ts (base mais overrides 7k/betpontobet/donald) fica só com metadados e o loader de page.$slug.tsx manda pro client apenas o HTML da página visitada.
Efeitos e DevPanel removidos (PR #1836) — 2edac1564: o ScrambledValue das estatísticas sai por completo (sem flag), o DevPanel é extinto das páginas (home, favoritos, wallet, histórico e página de jogo perdem os cards "DEV GET" e todo o plumbing, inclusive os payloads _debug nos loaders e os fetches dev-only extras), sobrando só /debug e as rotas dev.*, que já são gated em route-generation time. Validado no build de produção: zero ocorrências de DevPanel, DevApiExplorer ou dev-proxy nos chunks. O mesmo commit remove @cactus-agents/mocks do package.json do base — o pacote não tinha nenhum import no repo. (Os paths dele no tsconfig são regenerados a partir do core e ficam inofensivos.)
Flags mortas e dependências órfãs (onda 2/3) — 67ccc217a: showWalletRecentActivity DELETADA (o comportamento vira padrão do projeto), ConfigCat extinto — estava enabled: false em 100% das brands desde sempre, então saem a dependência configcat-js-ssr (124 KB de bundle), os hooks useConfigcat/Flag/Flags, o type, as 14 configs por brand e os campos featureFlags.configcat; os kill-switches remotos de marketing continuam funcionando pela API legada de feature flags. hashGtmPii também é DELETADA por ser declarada e nunca lida — vale notar que o hashing de PII entregue na semana anterior nunca dependeu dessa flag. Saem ainda @iconify-json/twemoji, @tailwindcss/aspect-ratio e jsdom (o único teste que o usava migrou pro happy-dom default).
Sportsbook e artes de liga saem do caminho crítico — b7094830c (PR #1855) torna os wrappers de sportsbook lazy: o _layout de sports importava os QUATRO estaticamente, então toda brand baixava ~270 KB do dist do sports-rogue (mais os outros SDKs) no load de /esportes mesmo usando outro provider; agora só o chunk do activeProvider desce (preload de /esportes de 2275 KB pra 1670 KB raw). 4131e5d80 (PR #1856) converte as artes full-color de liga acima de 4 KB (até 28 KB cada) em asset estático via ?url — antes elas entravam DUAS vezes no payload de toda página, inline no HTML SSR e de novo no chunk icons; 213fc17b9 (PR #1858) devolve a tintura pela cor da brand a essas artes.
Requests duplicadas (PR #1857) — c65fd057c reúne seis fixes de uma varredura de 30/07, dois deles reveladores: /api/games/vote era chamado 2× por pageview porque useVoteCountFallback estava em GameDetailsBar E GameDetailsMobile, montados juntos e escondidos por CSS; e dois POSTs idênticos em /api/rewards/list na hidratação de auth, resolvidos fazendo useHeaderNotificationsSync publicar pagination.total na store do badge.
Dois incidentes de resolução de módulo em produção
Prod e dev --registry não resolviam do registry (PR #1842) — da237f03f: o resolve.tsconfigPaths nativo do Vite 8 aplicava os paths source-first do tsconfig.json também no build de produção. Com o core clonado ao lado, todo @cactus-agents/* importado pelo app resolvia pro source local, e os imports feitos de DENTRO do source do core (api-client → country-config) escapavam pra node resolution — dois module ids pro mesmo pacote e bundle DUPLICADO (country-config em 2×76 KB raw / 17 KB gzip cada, ambos eager). O invariante "prod = registry" só valia no CI por acidente, porque lá não há clone do core. O fix desliga tsconfigPaths em prod e com CACTUS_FORCE_REGISTRY=1, repondo o único mapping não-core com um alias explícito de ~/ pra app/.
…e esse alias quebrou todos os brand overrides em produção (PR #1846, deploy revertido) — 639a61ac5: o alias /^~\// roda ANTES de todos os plugins (resolve.alias precede até enforce: "pre") e reescrevia ~/config/X pra path absoluto antes do brandOverridesPlugin interceptar, então TODOS os overrides de brand (layout, tema, logo, legal) saíam silenciosamente do build de produção. Dev não quebrava porque o tsconfigPaths resolve no core do Vite, depois dos plugins — por isso o gap só apareceu no deploy do bet7k.cl. O alias virou o tildeAppResolvePlugin, posicionado DEPOIS do brand-overrides na cadeia. Verificado com ORIGIN_DOMAIN=cl.bet7k.com: o marcador único do override volta ao bundle e o logo.png da brand volta a ser copiado. Regra que este par de incidentes confirma: override de brand é substituição de arquivo resolvida por plugin, e qualquer coisa que mexa na ordem de resolução pode desligá-la sem erro nenhum.
Contra o BFF real, sem mock (PR #1707) — 17da14426 liga /documents/* na API real do BFF e remove o mock DIDIT. 54e8277e5 adiciona o kill switch de runtime RUT_VALIDATION, então o fluxo pode ser desligado sem deploy. fbcfe037e põe Ramo A e Ramo B no payload de register.
Um clique em vez de dois — 79c36e94e unifica a validação de identidade CHL (RUT mais data) em clique único; b858f6ebd dá mensagem amigável pra RUT inexistente (CL00404) e aca911f18 deixa clara a mensagem de identidade no submit do cadastro.
OTP de telefone desligado no Chile (PR #1851) — 9518e29aa: com a validação de identidade cobrindo o passo, o step de OTP sai do cadastro do cl-bet7k-com.
Tabs da área de usuário (PR #1859) — d1410ec9e: a área já era casca SPA na camada de navegação (herança do trabalho de user-data-out-of-ssr), então o que travava a percepção era o shell descontínuo. user.refers entra no layout de tabs — antes ficava na subtree do restricted-guard e header, banner e tabs desmontavam e remontavam a cada ida e volta — e o gate de conta restrita vira guard client no próprio refers. O gate mounted do user/_layout ganha flag de módulo, então reentrada na mesma sessão pula o frame de skeleton.
Prefetch de módulos (PR #1861, plano 2026-07-30-instant-area-navigation-plan.md) — e553e8b4b parte de um diagnóstico específico: o que trava a primeira navegação é o download em SÉRIE do chunk do route module (2 RTTs), não os dados. A estratégia é aquecer os módulos da área ao entrar nela via modulepreload do manifest do RR7, sempre pós-idle, sem tocar o vite.config.ts — o grafo de chunks fica intacto, respeitando o guard-rail do incidente de ciclos. Onda 1 nas tabs de usuário (prefetch="viewport", indicador ativo otimista via isPending, prefetch="intent" nas entradas frias) e onda 2 no hub de cassino.
Página de jogo (PR #1854) — acaca515b tira share e CTA do skeleton e faz o blur de fundo reusar a própria thumb.
cl-bet7k-com migra do WordPress pra API 360Content (PR #1864) — 08534c4e4: o wpApiBase da brand aponta pro clone WP-compatível api.360content.app, com a brand identificada por query param. ContentsConfig ganha dois campos opcionais — apiQueryParams (params extras em toda request de conteúdo) e disableCache, que bypassa o platform-cache no service e responde Cache-Control: no-store nas rotas de conteúdo, porque a 360Content gerencia o cache e a publicação precisa refletir na hora. Brand-gated em build-time: nenhuma outra brand muda de comportamento.
Hub ao vivo volta a existir em api_new (PR #1867) — ec1a6451e: o 301 do hub ao vivo pra categoria-umbrella nasceu porque o hub LEGACY tinha pouco conteúdo próprio; em api_new as rows vêm da curadoria do BFF, então o hub volta a renderizar. liveHubRedirectActive(mode) gateia o 301 no loader e no clientLoader e também o filtro do sitemap (casino.live volta ao sitemap em api_new), e resolveLiveNavHref(to, mode) devolve os links de nav ao hub. 579af6b0b corrige o active do live no header pra brands com o redirect ligado.
Rows curadas com header oculto — 7c812d23f cria HomeRowOverride.hideHeader, que esconde título, ícone e (em rows de categoria) setas e "ver todos", mantendo o carrossel. É presentation-only: analytics continuam vendo o título curado em item_list_name. Caso canônico: em api_new a curadoria do BFF sempre emite o título da row top-games ("Pagou Muito") e o backoffice não tem opção de removê-lo.
Resposta vazia não é cacheável — 77675ba23: uma resposta vazia de /api/games/top-games (falha transitória do BFF) ficava presa por 1 h em DUAS camadas, o HTTP cache do browser (max-age=3600) e o SWR de localStorage, que gravava o [] como fresco — a row "Pagou Muito" desaparecia e não revalidava nem no F5. Agora vazio responde no-store, entry vazia conta como stale e o fetch usa cache: "no-cache" pra furar HTTP cache já envenenado (self-heal de browsers afetados).
c16f293d4 dá slug por modo ao chip "Exclusivos 7k" e 0e65060fb (PR #1831) liga parada livre por default em todos os carrosséis de conteúdo.
@cactus-agents/games 2.2.0 — facets — core 68ec4ad: o BFF passou a devolver facets junto da listagem filtrada (providers com jogos na categoria e categorias com jogos no provedor, com icon e order). Tipos GameListFacets* novos, passthrough em transformGameListResponse e campo opcional em GameFilterResult. Ausente quando a query não tem filtro — o consumidor trata undefined como "sem informação", ou seja, tudo ativo.
@cactus-agents/country-config 0.14.0 e 0.15.0 — dois passos de tree-shaking — core 0b5bd5a marca o pacote sideEffects: false e faz o tsup emitir ESM multi-módulo com entries internos (catalog, phone, nationality), porque o dist era um bundle único e importar getCountryConfig (2 KB) arrastava o world catalog (~70 KB) junto. Core d2d78e5 vai além porque o rolldown não descarta sub-chunks não usados de um facade parcialmente usado: novos subpaths ./registry e ./validators isolam o catálogo, com a API do . inalterada. No base, caf7f58eb (PR #1845) tira o world catalog do caminho crítico do auth, consumindo pelos subpaths; f911e010b e 0e480e207 fazem os bumps (api-client 0.18.3 e 0.18.4 acompanham).
@cactus-agents/sports-rogue chega a 0.29.2 — cb69a06f5 (0.29.1: telemetria sem @sentry/browser) e 100655a6e (0.29.2: locales lazy mais code-splitting). Lembrete que vale repetir: este pacote não vive em front-cactus-core, é publicado de outro lugar, e os providers de sportsbook declarados em app/config/sports/provider.ts são hoje ["first", "altenar", "betby", "rogue"].
betpontobet-bet-br — 8faf5fc2a (PR #1828) fecha a paridade de home com a 7k mais depósito sugerido e token de dinheiro; e4bfd9c8c (PR #1822) entrega a página de esportes sem nav secundária, com footer compacto e sidebar que recolhe; 3c3c79809 (PR #1860) ativa a página /promocode; b6f445751 move o link de ouvidoria pra barra legal e 7c8487622 comenta o link "Denúncias" na configuração de links legais extras.
cl-bet7k-com / bet7k-cl — 3280987a9 (PR #1850) troca a imagem do selo de licença GCB pelo selo CGA e c29ed7079 (PR #1843) atualiza o link do certificado pra cert.cga.cw.
7k-bet-br — a366f7097 põe borda degradê no "Indique e Ganhe" da sidebar, 7a37a4b25 remove Torneios e Missões da sidebar, 00875e45c uniformiza os ícones entre sidebar, topo, rows e pills e adiciona a row "Exclusivos 7k".
Copy global via overrides do base — a04f13085 renomeia copy global por override de locale no base, em vez de bumpar o core. Vale como exceção consciente: a regra do projeto é que tradução nova vai pro @cactus-agents/i18n.
Layout, em todas as brands — 58ba480e6 compacta o bloco de licença, cf10073ce compacta a Central de Ajuda e remove breadcrumbs, e33ff0eef compacta o breadcrumb do torneio, 8a9944164 traz ligas Popular e top esportes pro rail recolhido do accordion e 8e26b5fdd torna a nav secundária de /esportes ocultável com footer compacto opt-in.
KV era provisionado silenciosamente errado — ops a19e98a: o passo de KV listava namespaces com npx wrangler kv namespace list sob WRANGLER_LOG=none, e no wrangler v4 isso silencia TAMBÉM o resultado da listagem (que sai pelo logger). LIST_RAW vinha sempre vazio, o passo abortava por continue-on-error e o Worker deployava SEM o binding PLATFORM_CACHE_KV — em todas as brands, sem erro visível (rastro nos logs de fi-7k-bet em 27/07 e betpontobet em 28/07). A listagem e a criação passam a usar curl direto na API da Cloudflare, com paginação e erro real exposto no log.
Deploy atrás de Cloudflare Access — ops 2ddcaf3 cria skip_asset_verify por env, porque o verify HTTP do sync de assets recebe o redirect de login em vez de 200 e falha hard; ativado no stage-7wins-cc-bluetec-live, que estava com deploy quebrado por isso. Ops f4bd12f conserta o wiring: cond && "" || X nunca retorna string vazia no GitHub Actions ('' é falsy e cai no ||), então a flag era ignorada — o ternário foi invertido pra cond != true && URL || "".
betpontobet ganha paridade de cache com a 7k — ops 327bc34 muda default_branch de main-fix pra main-7k e copia o bloco do prod-7k-bet-br: doc HTML 30d, .data 300s com 120s de browser, KV 30d, shared_public_doc_for_auth: true e o cache.yml byte a byte. Aplicado também no stage pra validar o doc longo antes de produção. Ops c8e76c0 desliga auto_deploy no env de produção da brand.
Geração de cache — um bump na semana: ops 3da93fc (apcsrasj-v19 → v20). A razão registrada no generation.yml é concreta: doc SSR envenenado no stage-7k, renderizado exatamente na janela do bug de brand overrides descrito acima, preso no doc-cache de 30 dias e com o endpoint de purge inacessível por estar atrás de CF Access. É o custo real de combinar janela longa de cache com um bug de build silencioso.
Deploy protection — ops db076a8 e 447529b ajustam required_team de bet7k-cl, prod-7k-bet-br e betpontobet-bet-br; ops 8cf0932 adiciona public_domain (bet7k.ng) ao env bet7k-ng pra robots e sitemap não apontarem pro domínio de stage.
Sem entregas emfront-tests-e2e, front-service-api, front-service-api-dev-proxy e fecadm nesta semana.