Conn2Flow
Navegação das docs

SDD

Batch Index — Archive (BATCH-094 a BATCH-178)

Batches arquivados BATCH-094 a BATCH-178 do conn2flow. Para o índice ativo, ver BATCH-INDEX.md.

Batches arquivados BATCH-094 a BATCH-178 do conn2flow. Para o índice ativo, ver BATCH-INDEX.md.

Batch Status Escopo Alvo de validação Observações
BATCH-094 complete Tradução Completa dos Templates de Páginas do Módulo "publisher-index" para o Inglês (req-094) VALIDATION-CHECKLIST.md#batch-094 Traduzir rótulos de campos, cabeçalhos, placeholders, descrições e opções de selects para o inglês nos arquivos de páginas HTML de recursos (adicionar, editar, clonar) do módulo publisher-index. Verificação: git diff --check OK, vitest 29/29 OK, PHPUnit 110/110 OK. Fechado em 2026-07-21.
BATCH-095 complete Tradução Completa da Editbar, Painéis e Overlays do Editor Visual para o Inglês (req-095) VALIDATION-CHECKLIST.md#batch-095 Traduzir todos os painéis dinâmicos (c2f-add-panel, c2f-backup-panel, c2f-tpl-panel, c2f-ai-panel), tooltips e títulos da barra flutuante (html-editor-floating-toolbar), estilização do Tailwind (html-editor-tailwind-styler), breadcrumbs e alertas para o inglês quando o idioma atual for o inglês. Verificação: git diff --check OK, node --check OK, vitest 31/31 OK, PHPUnit 110/110 OK. Fechado em 2026-07-21.
BATCH-096 complete Mapeamento Visual de Embeds, Proteção de Eventos, Suporte Híbrido a PDF (PDF.js, Google Viewer, Object Fallback) e Modal Estruturado (req-096) VALIDATION-CHECKLIST.md#batch-096 Invólucro atômico .conn2flow-embed-wrapper (badge + escudo .c2f-embed-shield + 4 alças de resize) para object/iframe/embed/video/audio e para o contêiner .conn2flow-pdfjs, tratado como bloco atômico (resolveEditable) e removido em TODA extração de HTML (save e snapshots de undo) — o banco recebe só a tag limpa; applyState/afterDomMutation re-envolvem (idempotente). Embeds dentro de widget/.c2f-dyn-box e iframes de sistema ficam de fora. Modal #c2f-he-embed-modal em 4 abas (fonte + seletor de arquivo do servidor, motores de PDF, iframe/mídia, <param>/fallback/estilos) com detecção automática de .pdf. 3 motores: <object>+fallback com estilos inline, PDF.js (data-pdf-*) e Google Viewer (URL absolutizada). Novo runtime de core gestor/assets/interface/pdf-viewer.js (canvas, toolbar, zoom, modos vertical/página, auto-load da lib) + injeção condicional: gestor_pdf_viewer_detectar()/_assets() (lib gestor) e gestor_pagina_pdf_viewer() no pipeline; montarPdfViewerHead() no srcdoc do editor clássico. Ver DEC-092. Cache-bust: biblioteca-html-editor 1.5.0, dashboard.json 1.0.12, motor ?v=c2f11. Validação: php -l 4/4, node --check 4/4, npx vitest run 50/50 (novo html-editor-embed.test.js 19/19), composer test 158/158 (novo PdfViewerAssetsTest 5/5). Deploy Update => Core + homologação runtime (3 motores em desktop e mobile) pendentes com o operador. Plano em BATCH-096.md. Fechado em 2026-07-29.
BATCH-097 complete Correções de Homologação do BATCH-096 (duplicação/vazamento de embeds, interatividade no site publicado, z-index do seletor de arquivos e preview ao vivo do PDF.js), Separação da Edição Avançada no Dropdown "Página" e Inserção de Embeds no Painel "+" (req-097) VALIDATION-CHECKLIST.md#batch-097 Causa-raiz da duplicação (evidência temp/html-output.html): a 2ª cópia do <object> estava fora do #c2f-page-content, como filho de #c2f-layout-root → salva em layouts.html, que reaparece em toda página e dentro do iframe da Editbar. Correções: insertionRoot() manda inserções sem alvo para o conteúdo (fallback deixou de usar document.body); isEditorOwned reconhece #c2f-site-toolbar/#c2f-device-preview/#c2f-save-loader (blinda wrap/resolve/extração); extração idempotente (limpa o leitor PDF.js renderizado e resíduos de invólucro); disable()/enable() tiram e recolocam o escudo (interatividade fora da edição); markup nasce com position:relative;z-index:1 (camadas absolutas do template capturavam o ponteiro); modal 1000000 × picker 1000060; ensurePdfViewer() carrega o runtime sob demanda (Motor B ao vivo); dropdown com "Editar Avançado" (admin-paginas) + "Editar Publicação Avançado" (publisher-pages, só com publicação); 5 tipos de embed nos dois painéis "+" abrindo o modal na inserção. Ver DEC-093. Cache-bust: biblioteca-html-editor 1.5.1, dashboard.json 1.0.13, motor ?v=c2f12. Validação: php -l OK, node --check 4/4, npx vitest run 58/58 (html-editor-embed.test.js 19→27), composer test 158/158. Deploy Update => Core + homologação runtime pendentes com o operador. Plano em BATCH-097.md. Fechado em 2026-07-30.
BATCH-098 complete Área de Transferência Persistente do Editor Visual — copiar em uma página e colar em outra (demanda direta do Chefe, 2026-07-30) VALIDATION-CHECKLIST.md#batch-098 O bloco copiado vivia só em memória e se perdia ao sair da edição (reload) ou trocar de página. Agora copySelected() grava HTML saneado em localStorage['c2f-he-clipboard'] ({html,ts,origem}), initClipboard() recupera no boot (botão "Colar" já nasce disponível) e um listener de storage acompanha outras abas. buildClipboardMarkup() remove UI de runtime (invólucro de embed, leitor PDF.js) e anotações dependentes da página de origem (data-c2f-variable, contenteditable), preservando data-c2f-marker (widget colado continua widget); remapPastedIds() evita colisão de data-c2f-widget-id ao colar na mesma página; colar SEM seleção insere no fim do conteúdo via insertionRoot(). Ver DEC-094. Cache-bust: biblioteca-html-editor 1.5.2, motor ?v=c2f13. Validação: node --check OK, npx vitest run 64/64 (6 casos novos em html-editor.live.test.js), composer test 158/158. Homologação runtime pendente com o operador. Plano em BATCH-098.md. Fechado em 2026-07-30.
BATCH-099 complete Upload e Gestão de Pastas Liberados no Modo Picker do Admin-Arquivos (demanda direta do Chefe, 2026-07-30) VALIDATION-CHECKLIST.md#batch-099 O gerenciador é o MESMO em todos os pontos de seleção de arquivo (imagem, PDF, vídeo…), mas o modo picker (?paginaIframe=sim) escondia o bloco normal-tools (Adicionar / Nova pasta / Selecionar todos) e removia o botão voltar da tela de envio — quem precisava enviar um arquivo tinha de abrir o módulo em outra aba. As duas remoções foram desfeitas; o restante do fluxo já era preparado para o iframe (#paginaIframe# no href do botão, atualizarAddHref() no JS acrescentando &paginaIframe=sim, #url# do voltar apontando para a listagem em modo picker). Diferenciações legítimas do picker foram mantidas (botão "Selecionar" no lugar de "Copiar URL", sem cache de última pasta). Ver DEC-095. Validação: php -l OK, composer test 158/158, npx vitest run 64/64; homologação runtime pendente com o operador. Fechado em 2026-07-30.
BATCH-100 complete Mídia Embutida: 403 em Arquivos com Espaço, Streaming (HTTP Range) e Dimensionamento do Áudio (homologação do BATCH-097, 2026-07-30) VALIDATION-CHECKLIST.md#batch-100 403 medido: AH10411: Rewritten query string contains control characters or spaces — o .htaccess injeta o caminho DECODIFICADO na query e o Apache ≥2.4.53 recusa espaços; atinge qualquer arquivo com espaço no nome (imagem, PDF, vídeo, áudio). O .htaccess do instalador JÁ traz a flag [B,L,QSA] que corrige (novas instalações não têm o defeito); apenas o mirror local antigo estava sem. Correções de core: arquivo_nome_sanitizar troca espaço por hífen na ENTRADA (upload/renomeação) e urlDeArquivo() codifica a URL devolvida pelo picker (o caminho segue como identificador cru). Streaming: arquivo-estatico.php ganhou Accept-Ranges/Content-Length, resposta 206 com Content-Range, 416 para faixa inválida e Content-Type sem charset em binários — sem isso não há seek e o Safari/iOS não reproduz. Áudio: embedDefaultSize() por tipo (áudio sem altura, vídeo 360px, iframe 400px, documento 600px), markup de áudio sem height e resize só na largura. Ver DEC-096. Cache-bust: biblioteca-html-editor 1.5.3, motor ?v=c2f14. Validação: php -l 3/3, node --check OK, composer test 165/165 (novo ArquivoEstaticoRangeTest 6/6 + caso de sanitização), npx vitest run 66/66, e verificação HTTP real no container (200 + Accept-Ranges, 206 + Content-Range, 416, CSS mantendo charset). Fechado em 2026-07-30.
BATCH-101 complete Embed sem Arquivo Escolhido não Emite Atributo de Fonte Vazio (homologação, 2026-07-30) VALIDATION-CHECKLIST.md#batch-101 Player de áudio inserido pelo painel "+" aparecia colapsado (~11px de largura). Causa: ao aplicar o modal sem escolher arquivo, buildMediaMarkup emitia src="" — o navegador resolve isso para a URL da PRÓPRIA página, tenta carregá-la como mídia, falha e o Chrome colapsa o player (além da requisição inútil). Os geradores passaram a OMITIR a fonte quando vazia (src de mídia, data do object, data-pdf-src do PDF.js e o src do Google Viewer). Ver DEC-097. Cache-bust: biblioteca-html-editor 1.5.4, motor ?v=c2f15. Validação: node --check OK, npx vitest run 68/68 (2 casos novos), composer test 165/165. Fechado em 2026-07-30.
BATCH-102 complete Memória da Última Pasta no Modo Picker do Admin-Arquivos (demanda direta do Chefe, 2026-07-30) VALIDATION-CHECKLIST.md#batch-102 O seletor de arquivos em iframe abria sempre na raiz: o cache da última pasta (localStorage[adminArquivosDir]) era lido e gravado apenas fora do picker (if (!cfg.paginaIframe) nas duas pontas, herdado do BATCH-090). Agora o cache vale também no picker, com a precedência correta — pasta explícita na URL (?dir=) > cache > raiz —, o mesmo contrato que a tela de envio já usava (cfg.dirExplicito). O PHP da listagem passou a expor dirExplicito/dirInicial a partir de ?dir= (validado por arquivo_caminho_relativo_seguro), o que também permite abrir o seletor numa pasta específica. Ver DEC-098. Cache-bust: módulo admin-arquivos 1.1.6→1.1.7. Validação: php -l OK, node --check OK, JSON válido, composer test 165/165; verificação funcional é manual (estado de front). Fechado em 2026-07-30.
BATCH-103 complete Filtro de Modulos no Menu Principal do Gestor (demanda direta do Chefe, 2026-07-30) VALIDATION-CHECKLIST.md#batch-103 Porta o comportamento do c2f-modules-filter da Editbar para o menu do layout administrativo (@[[pagina#menu]]@): campo de busca no topo do componente menu-principal-sistema (pt-br/en), itens filtrados por texto sem acento/caixa, bloco do grupo ocultado junto com o cabecalho quando fica vazio, aviso de nenhum modulo encontrado e Esc para limpar. Itens fixos (Dashboard e Sair) tambem sao filtrados (decisao do Chefe). JS no novo gestor/assets/global/admin.js, carregado junto do HTML do menu - a fila de assets nao serve porque gestor_pagina_menu() roda dentro de gestor_pagina_variaveis(), DEPOIS de gestor_pagina_extra_head_e_javascript() ter resolvido o <!-- pagina#js -->. Ver DEC-099. Validacao: php -l OK, node --check OK, npx vitest run 76/76 (novo admin-menu-filtro.test.js 8/8), composer test 165/165. Requer deploy Update => Core (o componente do menu vem do banco). Rodada 2 (mesma sessao): o filtro da EDITBAR - usado como base - tinha o bug oposto e foi corrigido (digitar pa nao encontrava Paginas), assim como a busca de MODELOS do editor classico; ambas passaram a usar o mesmo helper de normalizacao. Na aba Modelos tambem entraram o botao x de limpar (padrao do gerenciador de arquivos) e a correcao do salto de rolagem ao Carregar Mais (a lista inteira era escondida durante o AJAX, a pagina encolhia e o navegador voltava ao topo). Cache-bust adicional: dashboard.json 1.0.14, biblioteca-html-editor 1.5.5. Validacao final: npx vitest run 83/83 (novos dashboard.iframe-toolbar.test.js 5/5 e 2 casos em html-editor-vars.test.js), composer test 165/165. Fechado em 2026-07-30.
BATCH-104 complete Suporte Nativo ao Checkout Transparente e Tokenização na Biblioteca PayPal (req-098) VALIDATION-CHECKLIST.md#batch-104 Biblioteca PHP 3.1.0: client token, payment_source em Orders e subscriber.payment_source em Subscriptions, intent CAPTURE/AUTHORIZE, idempotência e helper para ordem aprovada/token/assinatura. Novo gestor/assets/paypal/paypal.js: loader deduplicado, Card Fields + fallback Hosted Fields, classes de validação e submit normalizado sem fabricar nonce. Validação: php -l/node --check/git diff --check OK; Vitest 90/90; PHPUnit 172/172 (4 skipped preexistentes). Homologação Sandbox pendente. Plano em BATCH-104.md. Fechado em 2026-07-31.
BATCH-105 complete Navegacao por teclado nos filtros de modulos da Editbar e do menu principal (demanda direta do Chefe, 2026-08-03) VALIDATION-CHECKLIST.md#batch-105 ArrowDown entra e avanca somente pelos resultados visiveis; ArrowUp retrocede e, no primeiro item, devolve o foco ao input; nao ha ciclo no ultimo item e Enter permanece nativo do link. Implementado em dashboard.iframe-toolbar.js e global/admin.js, sem mudanca de markup. Cache-bust do dashboard 1.0.14→1.0.15. Ver DEC-101. Validacao: node --check/JSON/git diff --check OK; testes focados 16/16; Vitest completo 93/93. Plano em BATCH-105.md. Fechado em 2026-08-03.
BATCH-106 complete Painel Flutuante de Opções de Exibição, Sidebar Lateral de CSS, Barra Superior de Navegação e Ação Substituir no Editor Visual (req-106) VALIDATION-CHECKLIST.md#batch-106 Novo botão c2f-tb-view-options (Editbar + topbar do editor dos módulos) abre o painel c2f-view-options-panel com dois toggles DESLIGADOS por padrão e persistidos em localStorage['c2f-he-view-options']. Ligados, os painéis que acompanhavam a seleção são REALOCADOS (mesmo nó, mesmos handlers): o styler vai para a c2f-he-css-sidebar (240px, cabeçalho fixo, encaixada abaixo da navbar e do offset da Editbar) e o breadcrumb/filhos para a c2f-he-element-navbar (44px, colunas 20%/80%). A sidebar ganhou classes Tailwind agrupadas por variante, bloco de classes customizadas, autocomplete instantâneo, campo de CSS inline, seção de valores manuais (25px, 1.5rem, #123456) e inspetor de getComputedStyle(); o dicionário de sugestões foi expandido (grid, flex, bordas, sombras, opacidade, arbitrários w-[350px]/bg-[#1a2b3c] e variantes). Nova ação .he-tb-replace troca o elemento selecionado pelo bloco da área de transferência (renumerando ids de widget) e seleciona o novo objeto. Ver DEC-103. Cache-bust: biblioteca-html-editor 1.5.6→1.5.7, dashboard.json 1.0.15→1.0.16, motor ?v=c2f16. Validação: node --check 5/5, php -l OK, JSON OK, npx vitest run 118/118 (novo html-editor-view-options.test.js 21/21), composer test 172/172. Requer deploy Update => Core (botões vêm de página/componente do banco) + homologação runtime. Rodada 2 (homologação do Chefe, mesma data): os painéis flutuantes legados (breadcrumb, filhos e styler sobre o elemento) foram APOSENTADOS — só aparecem dentro dos painéis fixos; cabeçalho dos dois painéis ganhou os botões c2f-he-panel-close (✕, desliga o painel) e c2f-he-panel-side (setas: sidebar esquerda⇄direita, barra topo⇄base), com a ancoragem persistida e o encaixe recalculado nos quatro arranjos; clique em qualquer ponto da Editbar passou a fechar TODA a UI flutuante (c2f-toolbar:ui-dismiss → dismissHostPanels + c2fEditor.dismissFloatingUi: Opções, "+", Backups, Modelos, IA, Código Customizado, modais de embed/edição e seletor de arquivos), o que não acontecia porque o clique ocorre dentro do iframe da barra e nenhum backdrop é atingido; clique DENTRO de painel/modal segue sem efeito. Regressão corrigida no caminho: closeEmbedModal zera isModalActive incondicionalmente e impedia o fechamento do modal de edição. Cache-bust adicional: biblioteca-html-editor 1.5.8, dashboard.json 1.0.17, motor ?v=c2f17. Rodada 3 (homologação do Chefe, mesma data): campo de CSS inline da sidebar virou CodeMirror (mode: css, tema do editor, 110px, criado sob demanda, com degradação graciosa para textarea); resize-follow dos painéis c2f-he-live-box corrigido em dois pontos, PRESERVANDO o acompanhamento do arraste — a conta continua partindo do fundo do corpo e passou a descontar alturaAposElemento() (o #c2f-ai-status sumia ao arrastar o canto), e o ajuste saiu do ResizeObserver para syncLiveBoxCodeMirrors(panel), também chamado na troca de abas e na abertura (o editor da aba "Modo" nascia pequeno); disable({manterPaineis:true}) mantém a Sidebar e a Barra na tela durante o preview desktop/tablet/mobile. Cache-bust final: biblioteca-html-editor 1.5.9, dashboard.json 1.0.18, motor ?v=c2f18. Validação final: npx vitest run 137/137, composer test 181/181. Plano em BATCH-106.md. Fechado em 2026-08-06.
BATCH-107 complete Hardening de Segurança, Mitigação de Vulnerabilidades e Saneamento do Core (req-107) VALIDATION-CHECKLIST.md#batch-107 Pacote consolidado de segurança (BL-001 a BL-010): CSPRNG em sessões/tokens, contenção de path traversal via realpath, ativação do CSRF em formulários/AJAX, hardening de CORS/API/OAuth2, cabeçalhos HTTP, integridade no instalador com SHA-256/lock, prepared statements e saneamento de código morto com preservação de APIs públicas deprecated. Homologação pós-entrega: transição CSRF do autoatualizador vinculada ao SID/estado, fallback global de login expirado, releases sem .git aninhado em vendor e restauração compatível de banco_smartstripslashes(). Validação final: composer test 185/185 OK, vitest 140/140 OK. Base consolidada em gestor-v2.9.30; shim de banco posterior, destinado ao próximo release. Plano em BATCH-107.md. Fechado em 2026-08-06, adendo em 2026-08-07.
BATCH-108 in-progress Desacoplar a linha 2.x do código PHP 8.5 e acabar com o 429 falso no deploy (req-108) VALIDATION-CHECKLIST.md#batch-108 Diagnóstico fechado em 2026-08-10; nenhum código alterado nesta rodada. bibliotecas/banco-v2.php declara @requires PHP 8.5+ e usa clone $this with {} (188/202) e pipe \|> (2210), mas o ambiente roda PHP 8.3.32 (Apache e CLI). api_rate_limit_check() carrega essa biblioteca DENTRO de um try; o require_once lança ParseError — que é Throwable e portanto é capturado pelo catch da linha 101 —, a função devolve false e a API responde 429 "Rate limit excedido". O INSERT nunca roda, por isso api_rate_limits fica vazia (0 linhas mesmo após dois 429 no access log). Tabela, índice único e config (rate-limit-max=100) estão corretos. Rastreio: banco-v2.php, interface-v2.php e admin-paginas-v2 nasceram no MESMO commit c8fefefa ("v2 expansion – PHP 8.5 support"), leva de testes feita na main cujo desacoplamento para a 3.0.x já havia começado; o api.php só passou a consumi-los em b64bc18d (BATCH-107), tornando o resíduo alcançável. Modelo decidido pelo Chefe — separação por linha de versão, SEM fallback: a linha 2.x (main/2.9.x) não conhece PHP 8.5 e usa só as bibliotecas antigas; a 3.0.x exige PHP 8.5 sempre e usa banco-v2. Nada de PHP_VERSION_ID ou código condicional servindo as duas. Quando a 3.0.x virar main, a estrutura antiga sai junto com a linha 2.x. Aplicado em main (2026-08-10): api_rate_limit_check() reescrita sobre banco.php com três desfechos distintos (true/false=429/null=503 com log da exceção), removidos banco-v2.php, interface-v2.php, modulos/admin-paginas-v2/, os registros em config.php e 68 registros de seed órfãos em PaginasData/VariaveisData/ComponentesData (reserializados com json_encode do PHP para preservar o escape \/); ftp.php:139 corrigido. Lint do core: 100% compila sob 8.4 (antes 4 falhas). Contagem validada contra o banco real (1→2→3; 429 legítimo preservado). 3.0.x intocada. Falta replicar em 2.9.x e o deploy Update => Core pelo operador. Armadilha registrada: gestor_incluir_biblioteca() marca bibliotecas-inseridas ANTES do require (gestor.php:721), então include que falha fica registrado como carregado. Intake em req-108.md; validação em VALIDATION-CHECKLIST.md#batch-108.
BATCH-109 complete Correções de Cookies, Crawlers, Audit CSRF em global.js e Fixes no Editor Visual (req-109) VALIDATION-CHECKLIST.md#batch-109 M1: gestor_crawler_detectar() (29 tokens, pura) + gestor_requisicao_crawler() memoizado; gestor_cookie_verificacao($exigirSessao=false) com 3 desfechos — crawler não recebe cookie nem 302, página pública recebe Set-Cookie na PRÓPRIA resposta (sem interromper o carregamento) e só área restrita (gestor_permissao(), signin/signup) mantém o round-trip _gestor-cookie-verify/; crawler que caia nessa rota volta para a URL de origem em vez de cookies-is-mandatory/. M2: gestor_pagina_sistema_sem_rastreamento() + gestor_rastreamento_remover() (puras) zeram project-javascript e retiram blocos de GTM/GA/Meta Pixel das filas de head/JS nas páginas de sistema; gestor.rastreamentoBloqueado neutraliza fbq/dataLayer/gtag no global.js sem sobrescrever coletor já carregado. O core não contém snippet de Pixel — a dedup do ID em si é do projeto (registrado no batch). M3: entrypoint.sh/gerenciar-sites.sh garantem logs graváveis em [REDACTED]; log.php cria com 0777 + @chmod, desvia falha para error_log (o aviso saía no meio do HTML) e aplica 0666 na criação do arquivo; default de html_editor_ia_prompt() perdeu o resíduo sobre $modelo_texto (variável inexistente). M4: causa-raiz do 403 no salvamento — $(form).submit() usa a propagação SIMULADA do jQuery e cai em HTMLFormElement.prototype.submit(), e nenhum dos dois dispara evento submit, então o listener nativo de captura nunca anexava _csrf_token. global.js passou a cobrir os três caminhos (captura nativa, handler delegado do jQuery e envelope do prototype, instalado uma vez) e a ler o token também de gestor.csrfToken/parent.gestor.csrfToken (o srcdoc herda a origem, não o <head>); nova moduloUrl() elimina o admin-paginas/editar//; renderWidgets do iframe envia o token no cabeçalho e no corpo; previsualizarConfirmar anexa o campo e avisa em pt-br/en em vez de deixar o navegador exibir o JSON cru; gestor_csrf_resposta_invalida() devolve página HTML em navegação normal e preserva o JSON em AJAX. Campo mantido como _csrf_token (contrato do BATCH-107), não _gestor-csrf-token do intake. M5: gestor_open_graph_tags()/gestor_open_graph_existe() (puras) + gestor_open_graph_dados() com fallback pagina#og → nome da página → config.php (novas chaves site-description/site-og-image); tag vazia nunca é emitida e OpenGraph já presente no html_extra_head não é duplicado; gestor_roteador_crawler_pagina_protegida() devolve 200 só com <head> (sem incluir o módulo da página) para bot sem sessão, preservando /signin/ para humanos. Ver DEC-104. Validação: php -l 6/6, node --check 2/2, bash -n 2/2, git diff --check OK, composer test 200/200 (novo CrawlersOpenGraphTest 15/15), npx vitest run 171/171 (novos global-csrf.test.js 14/14 e html-editor-csrf-url.test.js 14/14). Deploy Update => Core + homologação runtime pendentes com o operador. Plano em BATCH-109.md. Fechado em 2026-08-13.
BATCH-110 complete CRUD de Metadados da Página, Imagem de Destaque, UI na Editbar/Editor e Sitemap XML (req-110) VALIDATION-CHECKLIST.md#batch-110 Continuação direta do BATCH-109: o que lá era só fallback agora tem origem por página. M1: migração 20260813120000 acrescenta imagem_destaque (varchar 500), og_titulo (255) e og_descricao (text) a paginas; guarda-se o CAMINHO do arquivo, não o id_arquivos (árvore física do BATCH-090). As três colunas entram em adicionar/editar/clonar do admin-paginas — na edição a comparação usa o valor do request sempre presente, e não isset, senão limpar o título social nunca gravaria vazio. O roteador seleciona as colunas e preenche $_GESTOR['pagina#og'] via gestor_pagina_og_do_registro() (pura), que devolve SÓ as chaves preenchidas: chave presente e vazia venceria o nome da página dentro de gestor_open_graph_dados(). M2: novo componente html-editor-seo (pt-br/en) e aba "SEO & Compartilhamento" no html-editor, entre blocos removíveis no padrão do publisher — html_editor_componente() só a monta quando o chamador informa seo, então layouts/componentes seguem iguais. A imagem usa o imagepick de interface_formulario_campos(), que resolve o placeholder depois que o componente já está em $_GESTOR['pagina'] — sem código novo de picker. 9 variáveis novas; html-editor-seo registrado com 1.0/checksums vazios e html-editor com checksums esvaziados (pt-br 1.24→1.25, en 1.10→1.11). M3: botão c2f-page-config-btn na Editbar → c2f-toolbar:page-config → painel na página HOSPEDEIRA (é lá que o seletor de arquivos pode cobrir o conteúdo; dentro do iframe da barra ficaria preso à altura dela), com título/descrição/imagem, overlay do admin-arquivos e o mesmo contrato de seleção do picker do editor. dismissHostPanels() fecha o painel, MENOS com o seletor aberto — senão o clique na barra derrubaria o que já foi digitado. Endpoints site-toolbar-page-config e -save atrás de gestor_acesso('editar','admin-paginas') + isolamento multiusuário dos backups, com o caminho saneado por arquivo_caminho_relativo_seguro(). M4: nova biblioteca sitemap.php (registrada em config.php) gravando na raiz pública, servida direto pelo .htaccess (!-f). sitemap_pagina_elegivel() exclui sistema, permissão restrita, inativas, rotas utilitárias (reusa gestor_pagina_sistema_sem_rastreamento() do BATCH-109) e fora da janela de publicação; montagem/upsert/remoção são funções puras sobre string, preservando byte a byte as entradas não alteradas; arquivo ausente ou corrompido dispara geração completa. Acionado por chamada direta no adicionar/editar/clonar e por callbackFunction no status/excluir da interface; falha de sitemap nunca interrompe o CRUD. Ver DEC-105. Cache-bust: dashboard.json 1.0.18→1.0.19, página dashboard-site-toolbar 1.17→1.18. Validação: php -l 10/10, node --check 4/4, JSON OK, git diff --check OK, composer test 223/223 (novo SitemapTest 19/19 + 4 casos em CrawlersOpenGraphTest), npx vitest run 181/181 (novo dashboard.page-config.test.js 10/10). Migração Phinx + deploy Update => Core + homologação runtime pendentes com o operador. Fora de escopo com motivo registrado: o formulário do publisher-pages (as colunas já valem para as publicações, que gravam na mesma tabela; falta só a aba no CRUD daquele módulo). Plano em BATCH-110.md. Fechado em 2026-08-13.
BATCH-111 complete Reversão do bloqueio de analytics e fim do laço de verificação de cookie (CR-001) VALIDATION-CHECKLIST.md#batch-111 Reverte o req-109 §3/§4 por decisão do Chefe. O intake partia de diagnóstico invertido: a cookies-is-mandatory/ não aparecia nos relatórios porque o analytics rodava nela — todo cliente sem cookie era EMPURRADO para lá. Evidência medida em produção (2026-08-13, antes de qualquer deploy): navegador real chega a 200 em 2 saltos; cliente stateless e Googlebot entram em laço infinito (/ → _gestor-cookie-verify → cookies-is-mandatory → _gestor-cookie-verify → …), com curl abortando por excesso de redirects. Causa: a própria cookies-is-mandatory/ é uma página e reentra em gestor_cookie_verificacao() ao ser renderizada. O mesmo header("Location: …"); exit; incondicional está em HEAD — o defeito não é das versões antigas. (1) Reversão: removidos o filtro de filas, gestor_rastreamento_remover(), o flag rastreamentoBloqueado e os 6 casos de teste que os cobriam; gestor_pagina_sistema_sem_rastreamento() renomeada para gestor_pagina_rota_sistema() (o nome virou mentira sem o bloqueio) e preservada porque o sitemap_pagina_elegivel() do BATCH-110 a usa. (2) Fim do laço: a decisão virou gestor_cookie_verificacao_desfecho(), função PURA com três desfechos (ignorar/emitir/redirecionar); rota de sistema cai em emitir mesmo com exigir_sessao=true, que é a linha que fecha o laço. Extraída para função pura de propósito: defeito medido merece teste que falhe se voltar. gestor_permissao() deixou de chamar gestor_cookie_verificacao(true) — redundante, quem chega sem sessão vai para /signin/ e é lá que a prova acontece. (3) noindex: rotas de sistema ganham noindex, nofollow + X-Robots-Tag. (4) Tokens em duas camadas: baseline embutido SEMPRE ativo, ampliado de 29 para 50 tokens (entraram adsbot-google, mediapartners-google, googleother, chrome-lighthouse, ahrefsbot, semrushbot, monitores de uptime); mais lista extra editável em Ambiente → Configurações do Site, desligada por padrão, com normalização de vírgula/;/quebra de linha. O baseline continua embutido porque a detecção ainda serve ao OpenGraph de página protegida (req-109 §10) — desligar tudo por padrão regrediria isso em silêncio. Ver DEC-106. Cache-bust: admin-environment 1.0.0→1.0.1. Validação: php -l OK, node --check OK, JSON OK, git diff --check OK, composer test 229/229 (CrawlersOpenGraphTest 15→25, com 5 casos dedicados à blindagem do laço), npx vitest run 178/178. Deploy Update => Core pendente com o operador — prioritário, é o que tira os sites do laço. Plano em BATCH-111.md. Fechado em 2026-08-13.
BATCH-112 complete Sitemap em assets, limpeza de URL antiga, correção do 301, aba SEO no publisher-pages, isolamento do painel da Editbar e meta tags de SEO (req-112) VALIDATION-CHECKLIST.md#batch-112 M1: sitemap_caminho_arquivo() foi da raiz para assets/ — na raiz dependia da regra !-f do .htaccess, que varia por instalação; em assets/ quem entrega é o arquivo-estatico.php do core, cujo default: já resolve extensão desconhecida contra assets-path e já mapeia xml, então NADA precisou mudar no controlador nem no .htaccess. sitemap_sincronizar_pagina()/_por_id() ganharam $caminhoAntigo: ao trocar o slug a URL antiga sai ANTES de a nova entrar. Elegibilidade deixou de olhar tipo — /signin/, /signup/ e /forgot-password/ entram; o painel continua fora por exigir permissão. Ajuste com motivo: só abrir para tipo=sistema faria entrar 13 rotas que NÃO são conteúdo (callbacks de OAuth, forms-submissions-process, signin-2fa, confirmações) — daí a nova sitemap_caminho_nao_indexavel(). M2: o 301 usava interface_modulo_variavel_valor(), que chama gestor_redirecionar_raiz() quando não acha o registro (um exit no meio da gravação, abortando o 301 sem aviso) e aplica filtro por id_hosts inválido aqui; trocado por banco_select_name direto, com anti-duplicata (A→B→A→B gerava duas linhas) e log em vez de derrubar a requisição. Replicado no publisher-pages. M3: cinco colunas de SEO + aba "SEO & Compartilhamento" nas três telas do publisher-pages, mais sincronização de sitemap em adicionar/editar (com limpeza da URL antiga)/clonar/status/excluir. M4: no ramo de fallback físico do image picker $fileId nunca era definido e $found=true pulava o bloco de padrões — warning na troca de #file-id#; passa a receber o caminho relativo (que É o identificador desde o BATCH-090). M5: causa-raiz foi c2f-page-config-panel/-picker NÃO estarem em isEditorOwned() (omissão do BATCH-110) — o motor usa elementsFromPoint+isEditorOwned, então o hover realçava o elemento ATRÁS e o 1º clique era consumido pela seleção. Registrados nos três pontos do contrato de UI, mais z-index/pointer-events/isolation e barreira de eventos na fase de BOLHA (em captura o stopPropagation impediria o evento de chegar ao próprio botão — há teste cobrindo). M6: migração 20260814100000 com meta_descricao e meta_keywords; gestor_meta_seo_tags(), gestor_meta_keywords_normalizar() e gestor_meta_seo_existe() (puras); fallback em cascata metadado próprio → og_descricao da página → config.php (nova chave site-keywords); campos nos três formulários; keywords vazia não emite tag. Extra (pedido do Chefe na rodada): o c2f-editbar-save era a terceira superfície de gravação e a única sem sincronizar o sitemap — verificado que ele grava só conteúdo (não sobrescreve metadados nem toca no caminho), mas atualiza data_modificacao, deixando o <lastmod> defasado; chamada acrescentada com falha isolada. Ver DEC-107. Cache-bust: dashboard.json 1.0.19→1.0.20, publisher-pages.json 1.0.0→1.0.1, componente html-editor-seo 1.1→1.2. Validação: php -l 10/10, node --check 2/2, JSON OK, git diff --check OK, composer test 241/241, npx vitest run 181/181. Migração Phinx + deploy Update => Core + homologação pendentes com o operador. Plano em BATCH-112.md. Fechado em 2026-08-14.
BATCH-117 implemented-pending-homologation Captura do CSS compilado, runtime do Tailwind na Editbar e painel de Código (req-117 + findings F1/F4c) VALIDATION-CHECKLIST.md#batch-117 O diagnóstico do intake estava incompleto. Lendo o bundle real do @tailwindcss/browser@4.3.0: ele prefixa @import "tailwindcss"; sozinho quando o contrato não traz um (logo o browser-contract.css sem import está correto), e cria a folha de saída vazia via document.head.append, escrevendo nela só quando o build assíncrono termina. O critério antigo — "a última <style> do <head> com regras" — escolhia qualquer folha injetada em runtime, e o html-editor.js injeta 4 no mesmo <head>: o risco não era só @layer utilities vazio, era gravar o CSS da UI do editor no banco. Os dois modos de falha estavam materializados na base photon ao mesmo tempo: sobre e pagina-raiz-do-sistema com o CSS autoral re-serializado e zero @layer; teste-de-pagina com @layer utilities vazio. M1: a captura virou window.HtmlEditorCssCapture no motor — UMA implementação para os dois editores (o clássico alcança pelo contentWindow do srcdoc, que herda a origem; a Editbar roda na mesma janela). A folha é reconhecida pelo FORMATO da v4 (camadas nomeadas) e três famílias de marcação saem da busca: data-c2f-tailwind-role, data-tailwind-role e o novo data-c2f-css-role (filas de CSS autoral e css_compiled da página pública) — sem a última, a Editbar releria o próprio valor antigo como compilação nova. Polling de 40×100 ms; janela esgotada preserva o valor anterior. M2: site-toolbar-render passou a devolver css, css_compiled, html_extra_head, framework_css e o contrato @theme static (que viaja no endpoint, não na página pública — só quem entra em edição precisa dele); activateEditor injeta contrato + CDN; performSave anexa css_compiled e os campos do painel, e só os que foram editados. Política do css_compiled (DEC-111): grava o DELTA contra a cascata REAL. html_editor_css_precompiled_baseline() concatena layout + recurso no editor clássico; na Editbar o baseline vem do próprio DOM. Página sob layout pré-compilado grava só as utilities novas; página sem cascata offline grava tudo e continua autossuficiente — sem flag. Correção descoberta medindo com Chromium real: assinatura de regra não segura camada de fundação (o Preflight do 4.3.0 traz ::file-selector-button, o bundle offline não → nada casava, economia de só 17%, e o css_compiled vem DEPOIS na cascata, então venceria o build em produção). theme/base passaram a ser decididas por CAMADA → economia final de 65% (8.774 → 3.086 bytes). M3: botão #c2f-code-btn (pt-br/en) → c2f-toolbar:edit-code → painel #c2f-code-panel com 4 abas CodeMirror, em isEditorOwned() nos três pontos. Diferença deliberada do intake: CSS ao vivo (debounce 400 ms), HTML por botão — reescrever #c2f-page-content a cada tecla recriaria os nós e derrubaria as anotações do mapeamento in-place. Findings F1/F4c: guardas puras em tailwind-recursos.php (tokens de @theme podados, utilities v3 removidas, recurso com utilities sem framework_css), avisando por padrão e com --tailwind-strict para promover a erro; heurística calibrada de 176 para 4 avisos. Ver DEC-111. Cache-bust: biblioteca-html-editor 1.5.11→1.5.12 (token interface mudou, governa os 3 consumidores), dashboard.json 1.0.20→1.0.21, página dashboard-site-toolbar 1.19→1.20 (pipeline publicou 1.21). Validação: php -l OK, node --check OK, JSON OK, composer test 297/297 (novos TailwindGuardasTest 16/16 e HtmlEditorBaselineTest 9/9), npx vitest run 220/220 (novo html-editor-css-capture.test.js 21/21 + 12 casos em dashboard.toolbar.test.js), gerador do core 175 recursos/0 erros, sync + --tables=paginas --force-all no [REDACTED]-local e GET /photon/sobre/ → HTTP 200. Pendente: o dado gravado só se corrige em um novo salvamento (não há migração); homologação runtime com o operador. Plano em BATCH-117.md.
BATCH-118 implemented-pending-homologation Findings restantes do review de 2026-08-15 (F2, F3, F4, F7–F10) + homologação do BATCH-117 VALIDATION-CHECKLIST.md#batch-118 Fecha o que o BATCH-117 deixou aberto do review. F2: o descarte de resource-precompiled em modo bundle passou a ser logado (rota + contagem, uma vez por requisição) — é o bucket de quem não declarou papel, por onde entra template escolhido em RUNTIME (SELECT ... WHERE target=...), que nasce fora do bundle e renderiza sem estilo. Dependência por target ficou como dívida; o log tira o defeito do silêncio, que era a urgência. F3: tailwind_recursos_layout_display_sensivel() (pura) detecta no CSS compilado do layout a combinação que gera o conflito (display sob @media + display incondicional concorrente) e avisa toda página sem tailwind_bundle servida por ele. Calibração: nenhum layout do core dispara; photon-admin e photon-public do [REDACTED] disparam — e photon-admin é onde o defeito foi medido. F4 (correção ao review): os dois pontos de bibliotecas/gestor.php NÃO são "includes de template", estão dentro de gestor_layout() — o papel certo é layout-precompiled, não dependency-precompiled. Isso agrava o finding: sem papel, o pré-compilado ia para o FIM da ordem e a cascata de theme/base/Preflight ficava invertida. F7: confirmation/success/error/failure/cancel viram sufixo (-, _ ou /) e payment/checkout/processing viram segmento final de caminho composto (payment/ na raiz continua indexável). Medido: sitemap do [REDACTED]-local de 36 → 31 URLs, com as 4 telas do review fora. F8: sitemap_legado_remover() apaga o sitemap.xml da raiz a cada gravação, com trava sitemap_conteudo_proprio() — arquivo de terceiro ou índice de sitemaps é preservado e logado. Medido: o de 1.161 bytes do local foi removido. F9: sitemap_robots_montar()/_gravar() geram assets/robots.txt junto da geração completa; verificado por HTTP que /robots.txt → 200 text/plain pelo default: do arquivo-estatico.php. Prefixos barrados saem das mesmas rotas que o sitemap exclui. F10: eram DUAS consequências, não uma — além do caminho reciclado apontar para a página errada, só o primeiro IDIOMA ganhava 301 (paginas_301 não tem coluna language e o caminho é agnóstico). gestor_pagina_301_registrar() deduplica pelo par (caminho, id_paginas) e substitui o bloco duplicado nos dois módulos; o roteador deixou de usar [0] e varre do mais recente ao mais antigo até achar página ativa no idioma corrente. Homologação do BATCH-117 na mesma rodada: painel de Código não fechava ao clicar fora (virou overlay com backdrop, no padrão do c2f-ai-panel) e nascia colado na borda direita (passou a ser centralizado); e — o mais grave — o salvo renderizava diferente da edição: descartar @layer theme inteiro era o F1 batendo na própria captura, porque o theme do layout só tem os tokens que o LAYOUT usa. Medido com Chromium real: 13 de 21 elementos divergentes (font-size 48px→16px, color→preto). O corte no theme passou a ser por DECLARAÇÃO (filterThemeRule): 0 de 21 divergentes, 3.695 bytes contra 8.774 do output completo. Validação: php -l OK, node --check OK, composer test 315/315 (novos casos em SitemapTest 24→38 e TailwindGuardasTest 16→20), npx vitest run 228/228, gerador do core 175/0 erros, e verificação HTTP no [REDACTED]-local (robots 200, sitemap 31 URLs, legado removido, /sobre/ 200). Plano em BATCH-118.md.
BATCH-119 implemented-pending-homologation Layout administrativo em Tailwind, menu interativo e painel de perfil com gestão de sessões (req-118) VALIDATION-CHECKLIST.md#batch-119 O bloqueio que definiu o desenho: interface.js chama $.fn.modal do Fomantic em 16 pontos sem guarda — inclusive no modal de Área Restrita, que é trava de credenciais —, e gestor_pagina_css()/_extra_head_e_javascript() usavam a mesma condição para CSS e JS, então um layout tailwindcss perderia os dois. Decisão do Chefe: Tailwind puro, com interface.php resolvendo por framework. M1: gestor_framework_css_resolver() (pura) decide a partir de layout + página (as duas colunas framework_css), devolvendo fomantic-ui/tailwindcss/hibrido; o bloco duplicado nos dois pontos de gestor.php foi eliminado. M2: interface_componente_variante()/_canonico() dão a cada componente um irmão <id>-tailwind (5 novos, pt-br e en) escolhido em runtime — em hibrido a variante NÃO é aplicada, porque com Fomantic na página o legado é o único com estilo garantido; os switch seguem no id canônico (a variante muda o HTML, não o contrato). interface_assets_incluir() unificou as 9 cópias do mesmo par de linhas e escolhe interface-tailwind.js. Nenhum componente legado foi alterado. O runtime novo (vanilla) lê o MESMO dicionário de regras do validador do Fomantic — o inventário inteiro usa só 6 tipos —, então nenhuma função PHP de validação mudou; loader com contador (2 AJAX simultâneos não apagam o loader na 1ª resposta) e Área Restrita que não fecha por Esc nem por clique no fundo. M3: layout-administrativo-tailwind (pt-br/en, HTML+CSS) com o legado intocado; carrega só components/icon.min.css do Fomantic — a folha de ÍCONES, não o framework, porque modulos.icone guarda nomes Fomantic de todos os módulos e trocar essa fonte quebraria plugins de terceiros. gestor_pagina_menu() escolhe menu-principal-sistema-tailwind + admin-tailwind.js (vanilla): overlay em mobile sem empurrar o conteúdo, resize 220–450px persistido, duplo clique restaurando o padrão, filtro sem acento/caixa e o teclado do BATCH-105. M4: painel em 3 abas (querystring → hash → localStorage; a querystring vence), medidor de senha com o piso real do backend (12 chars), step-up auth preservado, QR/segredo TOTP ainda só sob ?configurar-seguranca=sim; core ganhou usuario_user_agent_analisar()/usuario_sessao_formatar() (puras) + as 3 funções de sessão — as revogações exigem id_usuarios no WHERE (o pubID vem do cliente) e a sessão atual não tem botão de revogar. M5: tailwind_bundle não foi escolha estética — sem ele o compilador acusou o F3 do review de 2026-08-15 (layout emite display sob variante responsiva); com o bundle o aviso some. Validação: php -l OK, node --check 3/3, gerador 191/16 compilados/0 erros, composer test 353/353 (novos PerfilUsuarioSessoesTest 24/24 e FrameworkCssResolverTest 14/14), npx vitest run 309/309 (novos perfil-usuario.painel 25/25, admin-tailwind 24/24 e interface-tailwind 32/32). Deploy Update => Core + homologação pendentes. Plano em BATCH-119.md.
BATCH-120 implemented-pending-homologation Personal Access Tokens (PAT) e códigos de recuperação de 2FA (req-119) VALIDATION-CHECKLIST.md#batch-120 Dois segredos existem em texto puro UMA vez cada — o PAT na resposta da criação e os 10 recovery codes na resposta da ativação do 2FA; o banco guarda só hashes e não há endpoint de recuperação. Isso decidiu a interface: criar token não recarrega a página, e ativar o 2FA deixou de recarregar (o fluxo antigo fazia location.reload() e teria destruído os códigos antes de o usuário anotá-los). M1: migração 20260818100000 com usuarios_api_tokens (índice único em token_hash — a validação busca PELO hash e colisão faria duas contas dividirem credencial) e two_factor_recovery_codes em usuarios. M2: 8 funções puras + 4 com banco em bibliotecas/usuario.php. SHA-256 sem sal por decisão explícita (token é 64 hex de CSPRNG, não há dicionário a proteger, e um sal por linha obrigaria a varrer a tabela a cada requisição de API); status desconhecido falha fechado, data de expiração ilegível falha aberto (o defeito é do dado; derrubar credencial de produção por formato inesperado seria pior); expirado distinto de revogado porque o usuário resolve os dois de formas diferentes; alfabeto dos códigos exclui 0/O/1/I/L, que são os que se digita errado lendo de um papel, e cada erro custaria uma das dez chances. M3: api_token_validar_memoizado() desempata pelo formato (c2f_pat_) antes de validar — sem isso todo PAT passaria pelo validador de JWT e o usuário receberia "token inválido" sem pista do motivo; os dois validadores devolvem o MESMO contrato, então nenhum endpoint aprendeu formato novo e o rate limit por usuário passou a valer para PAT de graça. M4: aba 4 no painel Tailwind, exibida só a perfis autorizados pela MESMA política da API (AUTH_API_ALLOWED_PROFILES) e removida do HTML por modelo_tag_del quando não; revogar mantém a linha (apagar destruiria criação e último uso, que são a auditoria da credencial); no login, o recovery code só é tentado DEPOIS que o TOTP falhou — tentar antes gastaria um código por digitação errada. Validação: php -l OK, node --check OK, gerador 0 erros, composer test 387/387 (novos UsuarioApiTokensTest 20/20 e UsuarioRecoveryCodesTest 14/14), npx vitest run 328/328 (novo perfil-usuario.api-tokens 19/19). Fora de escopo com motivo: os escopos são gravados e devolvidos em scope, mas nenhum endpoint os aplica — a API autoriza por perfil, e mudar isso é contrato de API com intake próprio. Migração Phinx + deploy + homologação pendentes. Plano em BATCH-120.md.
BATCH-121 implemented-pending-homologation Layout público Tailwind e migração das 15 telas de identidade (req-120) VALIDATION-CHECKLIST.md#batch-121 Fecha o programa iniciado no BATCH-119. A migração é de MARCAÇÃO — o backend não mudou, o que desloca o risco: não há defeito lógico a caçar, e sim duas formas silenciosas de errar — classe do Fomantic sobrevivendo num arquivo (tela sem estilo em produção, já que as telas públicas deixaram de carregar o framework) e name de campo POST/bloco de template perdido na reescrita (formulário que envia e não grava nada, sem erro em lugar nenhum). Nenhuma das duas é pega por teste de comportamento, então a cobertura lê os arquivos REAIS de recursos nos dois idiomas e trava os dois contratos. M1: layout-pagina-sem-permissao-tailwind (pt-br/en, HTML+CSS) com o legado intocado; no CSS autoral só o que utility não expressa — o autofill do Chrome (que ignora background-color e exige sombra interna, senão todo login nasce com um campo destoando do card) e o espaçamento do campo de código 2FA. M2: 15 telas × 2 idiomas = 30 arquivos, GERADOS por script porque compartilham o mesmo esqueleto — escrever à mão faria o vocabulário divergir entre telas do mesmo fluxo, que é o defeito sendo corrigido. Area-restrita acompanha o layout ADMINISTRATIVO Tailwind (é step-up dentro do painel) e ganhou tailwind_bundle assim que o compilador acusou o F3 nela. M3: o HTML montado em runtime pelo PHP migrou junto, com vocabulário em constantes (PERFIL_PUBLICO_*) — alternador senha/e-mail, botões sociais, campo de código e reenvio de e-mail do 2FA, e a etiqueta de status dos metadados. Contrato com o JS preservado (.login-method-toggle, data-method, active): nenhum handler mudou. Botões sociais usam LETRA como ícone — as telas públicas não carregam Fomantic e uma folha de ícones inteira para dois glifos custaria mais que o botão. As 10 inclusões diretas de interface.js viraram interface_assets_incluir(), senão as telas migradas receberiam o runtime legado e quebrariam. Validação: php -l OK, node --check OK, gerador 32 compilados/0 erros, varredura com zero classe Fomantic nos 30 arquivos, composer test 494/494 (novo PerfilUsuarioTelasPublicasTest 107/107), npx vitest run 328/328. Fora de escopo com motivo: componentes de e-mail (exigem CSS inline e tabelas) e telas administrativas de outros módulos (dependem das variantes restantes de interface.php, registrado no DEC-113). Deploy + homologação pendentes. Plano em BATCH-121.md.
BATCH-122 implemented-pending-homologation Degradação graciosa quando a migração ainda não rodou (req-119) VALIDATION-CHECKLIST.md#batch-122 Correção de premissa minha: eu havia registrado "migração Phinx pendente" como passo manual — está errado, o pipeline roda sozinho (atualizacoes-banco-de-dados.php → migracoes() → $manager->migrate($env)), só pulando com --skip-migrate. O Chefe apontou o problema REAL por trás: código chega por ARQUIVOS e schema por MIGRAÇÕES, e não há garantia de que cheguem juntos — a migração pode falhar e o deploy prosseguir, a atualização pode ser só de arquivos, o operador pode pular, e existe a janela entre os arquivos chegarem e a migração terminar, em que toda requisição roda código novo contra schema velho. Sem tratamento: Table doesn't exist numa tela que já funcionava, por causa de funcionalidade que o usuário nem pediu (é a mesma lição do schema drift registrada no Photon). Mecanismo: gestor_schema_tabela_existe() (um único SHOW TABLES por requisição, resultado inteiro memoizado — checar tabela a tabela custaria uma ida ao banco por verificação, em código no caminho de render) e gestor_schema_campo_existe() (confere a TABELA antes, porque SHOW COLUMNS sobre tabela inexistente é erro de SQL e o objetivo é não produzir nenhum). Ambos falham fechado — banco indisponível, exceção ou detector ausente dão false. Onde entra: aba de Chaves de API não é renderizada; as 4 funções de PAT recusam antes de tocar o banco; a API devolve 401, não 500; o 2FA ativa normalmente e apenas não gera recovery codes (assimetria deliberada — perder o segundo fator por uma coluna ausente seria muito pior); resgate e rótulo do recovery code somem. usuario_recovery_codes_gerar() segue pura e independente do gate. Validação: php -l OK, composer test 508/508 (novo SchemaDegradacaoTest 14/14, simulando os dois mundos pelo cache de schema sem tocar o banco), npx vitest run 328/328, gerador 0 erros. Regra que fica: toda funcionalidade nova que dependa de migração precisa de gate que a esconda enquanto o schema não estiver pronto. Plano em BATCH-122.md.
BATCH-123 implemented-pending-homologation HTML para o Sistema de Recursos e classes para as Variáveis (correção do Chefe) VALIDATION-CHECKLIST.md#batch-123 Correção de rumo. Nos BATCH-119/120/121 eu escrevi markup direto no PHP ($html .= '<div class="…">') e fixei as classes utilitárias em constantes do arquivo (PERFIL_USUARIO_CARTAO…). As duas coisas violam a arquitetura: HTML pertence a resources/ (é o Sistema de Recursos que compila para *Data.json e sincroniza com o banco — markup no PHP não é editável por instalação nem aparece para o operador) e valor de apresentação pertence às VARIÁVEIS do sistema (dados de banco, versionados, editáveis por instalação). O .env/config.php NÃO era o destino: ele é para constante global estável ou dado sensível — distinção estabelecida pelo Chefe. Feito: 5 componentes novos × 2 idiomas (perfil-usuario-seguranca, -sessoes, -api-tokens, -2fa-campos, -login-metodos), cada um guardando TODAS as variantes da tela em blocos nomeados; o PHP passou a só escolher qual bloco entra e com que valores. 15 classes viraram variáveis por idioma, consumidas como @[[classe-…]]@. Verificação que decidiu o desenho: compilei um probe real e confirmei que o extractor do Tailwind enxerga classes dentro do JSON de variáveis — por isso perfil-usuario.json entrou em tailwind_sources. Medido: antes de declarar os componentes em tailwind_dependencies, divide-slate-100 (que só existe no componente de Segurança) ficava fora do bundle. A regra virou teste: PerfilUsuarioRecursosTest falha se o PHP voltar a montar tag ou declarar class=", se voltar const PERFIL_*, se um bloco usado pelo PHP sumir do componente (bloco ausente faz modelo_tag_val devolver vazio — a seção some da tela SEM erro), se um gancho de JS se perder, ou se o JSON deixar de ser fonte Tailwind. Escrever $html .= '<div…' é sempre mais rápido que criar o bloco no componente: é defeito que volta sozinho na próxima pressa, por isso a regra é executável. Validação: php -l/node --check OK, gerador 40 compilados/0 erros e 150 componentes, bundle conferido classe a classe, composer test 520/520 (novo PerfilUsuarioRecursosTest 12/12, 380 assertions), npx vitest run 328/328. Plano em BATCH-123.md.
BATCH-054 a BATCH-093 complete Batches históricos arquivados Ver arquivo histórico batches-054-093.md Os detalhes de implementação e escopo dos lotes 54 a 93 foram arquivados para manter a eficiência do contexto de IA.
  • BATCH-097 (externo, projeto [REDACTED] req-063): nova biblioteca core gestor/bibliotecas/stripe.php + asset gestor/assets/stripe/stripe.js + registro em config.php (bibliotecas-dados). Integração Stripe (Payment Element + Billing) nos moldes da biblioteca paypal; API fixada em 2024-06-20. Consumida pelos módulos de assinatura do projeto [REDACTED] (ver [REDACTED]/sdd/implementation/BATCH-106-stripe-gateway-integration.md e DECISION-036). php -l/node --check verdes. Autorizado pelo operador em chat (2026-07-31)

  • BATCH-097b (externo, projeto [REDACTED] req-063 lote 2): plataforma_gateways_stripe_webhook preenche o slot TODO case 'stripe' da plataforma de gateways (modular + legacy, validação HMAC via biblioteca stripe) e stripe_trocar_preco_assinatura na biblioteca (proration nativa). php -l verde. Ver [REDACTED]/sdd/implementation/BATCH-107-stripe-webhook-and-plan-change.md

  • BATCH-097c (externo, projeto [REDACTED] req-063 — correcao de bug do core): forms.widget.js e documentado como script EXCLUSIVO do preview do Editor HTML, mas forms_render() o incluia tambem no site publicado. Em pagina publica ele pedia config a rota administrativa /forms/ (401) e, no complete, carregava interface.js por cima do layout publico (Uncaught TypeError: $(...).modal is not a function) alem de recarregar formulario.js, duplicando handlers e podendo derrubar a submissao AJAX do formulario. Correcao em 2 camadas: (1) forms_render deixa de incluir o script — o preview ja o recebe via widget_js_include do html-editor; (2) guard no proprio JS: se gestor.form[formId] ja existe (site publicado) nao solicita config nem carrega bibliotecas. Bonus: campos password passam a sair com autocomplete="new-password", eliminando o alerta do Chrome. forms 1.1.0->1.1.1. php -l/node --check verdes

  • BATCH-097d (externo, projeto [REDACTED] req-066): seção "CATÁLOGO (Produtos e Preços)" na biblioteca core gestor/bibliotecas/stripe.php — stripe_criar_produto, stripe_atualizar_produto (merge parcial; limpar imagens exige images[]=''), stripe_consultar_produto, stripe_listar_produtos, stripe_arquivar_produto, stripe_criar_preco, stripe_consultar_preco, stripe_listar_precos e stripe_arquivar_preco. As listagens devolvem ['data','has_more'] e aceitam expand (o consumidor usa data.product para trazer o catálogo inteiro numa chamada). Não há update de unit_amount na API do Stripe: trocar valor/moeda/recorrência é criar preço novo e arquivar o anterior. Nenhum arquivo do core além da biblioteca foi tocado. php -l verde; smoke das pré-condições no projeto consumidor. Ver [REDACTED]/sdd/implementation/BATCH-117-stripe-products-module.md e DECISION-040

Batches em andamento

Batch Status Escopo Plano
BATCH-115 in-progress Extração de HTML dinâmico e consolidação do Tailwind por recurso (req-115) BATCH-115.md
BATCH-116 ready-for-intake Revisão dos Modos de IA (ai_modes), Diretrizes de CSS (3 camadas) e Contexto no Assistente IA (req-116) BATCH-116.md (relatório histórico não localizado)
BATCH-117 implemented-pending-homologation Paridade do Tailwind Browser CDN, Painel de Código na Editbar e Correção da Captura do CSS Compilado (req-117) + findings F1/F4c do review de 2026-08-15 BATCH-117.md
BATCH-118 implemented-pending-homologation Findings restantes do review de 2026-08-15 (F2, F3, F4, F7–F10) + correções de homologação do BATCH-117 BATCH-118.md
BATCH-119 implemented-pending-homologation Layout administrativo Tailwind, menu interativo e painel de perfil com sessões ativas (req-118) BATCH-119.md
BATCH-120 implemented-pending-homologation Personal Access Tokens e códigos de recuperação de 2FA (req-119) BATCH-120.md
BATCH-121 implemented-pending-homologation Layout público Tailwind e migração das 15 telas de identidade (req-120) BATCH-121.md
BATCH-122 implemented-pending-homologation Mover Página de Publicação entre Tipos de Publicador no Módulo Publisher Pages (req-121) VALIDATION-CHECKLIST.md#batch-122
BATCH-124 implemented-pending-homologation Correção da População de Publicadores de Destino no Modal Mover Publicação (req-122) VALIDATION-CHECKLIST.md#batch-124
BATCH-125 implemented-pending-homologation Customização de Menus Administrativos: Labels e Ordenação em Módulos Grupos e Override de Componentes por Projeto (req-123) VALIDATION-CHECKLIST.md#batch-125
BATCH-127 implemented-pending-homologation Correção de Reload em Erro de CSRF/Sessão, Mapeamento de Ícones de Projetos, Alternância de Botões Abrir/Fechar Menu e Eliminação de Warnings do Lucide (req-125) BATCH-127.md
BATCH-129 implemented-pending-homologation Extrator Semântico de Tokens do Tailwind para o Assistente de IA no Editor HTML (req-127) VALIDATION-CHECKLIST.md#batch-129 O Assistente recebia {{framework_css}} = tailwindcss e nada mais sobre a marca do projeto — o modelo respondia bg-red-600 onde o projeto tem bg-mp-red. Mandar o browser-contract.css bruto custaria 78 KB (~20 mil tokens) por interação. M1: html_editor_ia_extrair_tokens_tema() resolve o contrato pelo mesmo caminho do runtime do Tailwind Browser (contents/ do projeto na frente de assets/ do core) e delega para a parte PURA html_editor_ia_tokens_tema_compilar(). Medido: 78.485 → 1.482 bytes, 98,11% de corte no [REDACTED]. Duas decisões mudaram o resultado: o corte de valor é por FORMA (data:, url() longo, >120 bytes), o que tira as --art-*-mask com SVG embutido sem precisar de allowlist por nome; e o corte por orçamento é round-robin entre namespaces, porque na varredura sequencial as 63 cores do [REDACTED] consomem o teto inteiro e os 3 --shadow-* somem por serem poucos. A saída volta à ordem natural — o round-robin decide QUEM entra, nunca em que ordem sai. M2: html_editor_ajax_ia_requests() troca {{theme_tokens}} ANTES do switch de target, então os TRÊS escopos do intake são cobertos pela mesma linha (o editor clássico entra por html-editor-ia-requests; a Editbar por site-toolbar-ia-request → dashboard_ajax_site_toolbar_ia_request(), que só checa permissão e delega). Projeto sem contrato tem a seção INTEIRA removida via modelo_tag_del(): prompt mandando "usar prioritariamente" uma lista em branco é pior do que prompt sem a seção. {{css_compiled}} ficou opt-in e resumido (html_editor_ia_css_classes_resumir(), que desescapa .lg\:flex) — o valor cru devolveria o payload aos 20 mil tokens. M3: a fonte dos modos é resources/<lang>/ai_modes/, não o ModosIaData.json (que é artefato do gerador). Diretriz nos 6 .md (paginas, paginas-editbar, componentes × pt-br/en), version 1.0→1.1 e c2f resources:sync → 2.652 recursos / 0 erros. Validação: php -l OK, composer test 680/680 (novos HtmlEditorIaThemeTokensTest 39/39 e HtmlEditorIaRequestsThemeTokensTest 11/11), npx vitest run 337/337. Plano em BATCH-129.md.
BATCH-128 ready-for-intake Mapeamento Completo de Ícones Tailwind/Lucide para os Módulos Restantes do Projeto conn2flow-site (req-126) BATCH-128.md
BATCH-130 implemented-pending-homologation Blindagem de banco_num_rows, Proteção na Consulta de Hosts em perfil-usuario e Eliminação de Warnings de Chaves Indefinidas no Core (req-128) BATCH-130-safe-banco-num-rows-and-php-warnings.md banco_num_rows() devolve 0 para resultados inválidos; redefinição de senha consulta [REDACTED] com a tabela presente; campos opcionais e CSS compilado usam guards. Validação: lint OK, teste focado 5/5, suíte PHP 685/685.
BATCH-131 implemented-pending-homologation Blindagem e Correção de Inicialização em Publisher e Publisher Pages (Template Options, Nome na Clonagem e Prevenção de Publicações sem HTML) (req-129) BATCH-131.md (relatório histórico não localizado) Inicialização de $template_id_options e verificação is_array em publisher.php; extração de $nome e guards !empty() em publisher-pages.php; fallback server-side do template ativo para evitar páginas com HTML zerado.
BATCH-132 implemented-pending-homologation Fallback Automático para Template de E-mail, Blindagem de Configurações e Correção de CSRF/URL no Módulo Forms (req-130) VALIDATION-CHECKLIST.md#batch-132 Fallback robusto para forms-prepared-email, sanitização de subject e reply_to, fallback de form_action, normalização de URL (moduloUrl) e token CSRF explícito no AJAX do admin de forms. Validação: lint OK, testes PHP 7/7, testes JS 5/5, suíte PHP 697/697, suíte JS 342/342.
BATCH-133 implemented-pending-homologation Repasse da identidade do projeto ao atualizador de banco no deploy local (req-131) BATCH-133-project-identity-passthrough.md updates-manager-database.sh recebia --project (usava para resolver o dockerPath) e NAO o repassava ao PHP: o deploy local de projeto era tratado como atualizacao normal e ficava bloqueado pela marcacao project que ele mesmo gravou — em silencio, com o relatorio contando a linha como "sem alteracao" e fechando em sucesso. O caminho remoto (X-Project-ID -> api.php) nunca teve o defeito: era assimetria entre os dois deploys do MESMO projeto. A regra de sincronizarTabela() NAO foi tocada — o defeito era de alimentacao. Repasse condicionado e com validacao do identificador. Corrige as tarefas Projects - Update => Core e Projects - Synchronize => Database -> ID. Validacao: 5 testes novos, suite 702/702, e ponta a ponta no [REDACTED]-local sem --force-all.
BATCH-134 implemented-pending-homologation Limpeza do HTML entregue ao navegador — Sanitização de HTML e JS embutido (req-132) BATCH-134-html-sanitize-on-delivery.md Gate HTML_SANITIZE e HTML_SANITIZE_JS configuráveis via .env e tela admin-environment. Scanner com estado para higienização segura de JS e HTML inline. Medido: -33,5% no payload com DOM e scripts íntegros.
BATCH-135 implemented-pending-homologation Emissão Condicional de Cookies em Modo de Desenvolvimento, Comandos CLI de Gestão de Ambiente (env), Autenticação (auth:cookie) e Inspeção Headless (page:inspect) (req-133) BATCH-135.md (relatório histórico não localizado) gestor_cookie_is_secure() desativa Secure apenas em HTTP + DEVELOPMENT_ENV=true; comandos c2f env:status/env:set, c2f auth:cookie e c2f page:inspect com Playwright; 8 comandos project:* blindados com --project=ID. Validação: 21 testes novos, suíte 758/758.
BATCH-136 implemented-pending-homologation Resolução de Path de Mirrors de Projetos em auth:cookie e env:*, Execução via Docker e Suporte a path_tests (req-134) BATCH-136.md Resolvedor compartilhado path_tests → target → path; env:* --project; gerador auth:cookie via [REDACTED] com fallback local. Validação: 14 testes focados; suíte 765/765; runtime real no Photon aprovado.
BATCH-137 implemented-pending-homologation Controle de Animações do Sistema Operacional via CLI (c2f motion:on off status) para Validação de prefers-reduced-motion (req-135) BATCH-137.md Windows real ON → OFF → ON com restauração confirmada; Linux/macOS cobertos por runners injetados. Validação: 9 testes focados e suíte 774/774.
BATCH-138 implemented-pending-homologation Preservação do HTML para administradores no Live Editor e mapeamento de widgets vazios/na raiz do layout (req-111) BATCH-138.md Gate absoluto da higienização condicionado à Dashboard Site Toolbar; mapeamento e reconstrução de widgets vazios ou diretamente no #c2f-layout-root. PHPUnit 776/776; Vitest 345/345; runtime anônimo/admin e Playwright aprovados.
BATCH-139 implemented-pending-homologation Desacoplamento da limpeza de releases antigas dos scripts locais e migração pós-sucesso para GitHub Actions (req-136) BATCH-139.md Remove pré-exclusão destrutiva, publica branch/tag em push atômico e limpa somente patches anteriores da mesma série após a nova release. bash -n 5/5; YAML 2/2; simulação multi-série aprovada.
BATCH-140 implemented-pending-homologation Seleção em Lote no Iframe do Admin-Arquivos, Overlay Fixo nos Modos Compactos e Controle de Tamanhos no Galleries (req-137) BATCH-140.md Botão #c2f-pick-selected visível só no picker e com arquivo marcado, despachando UMA mensagem por arquivo (pastas de fora) — o canal postMessage tem seis consumidores que leem um objeto único por evento, e um array quebraria todos. .c2f-actions dos modos médio/pequeno virou overlay absoluto sobre a miniatura, com pointer-events: none no contêiner para não roubar o clique da miniatura nem do checkbox (que subiu para z-index: 5, acima do overlay). Medido no runtime: card 208x174 (médio) e 120x98 (pequeno) idênticos com e sem hover, mesmos x/y em todos os cards. Descoberto na própria evidência visual: todo renderLista() reconstruía o HTML e desmarcava os checkboxes enquanto a barra seguia contando — corrigido reaplicando o estado no DOM. No galleries, a classe de modo vive no contêiner #gallery-items (e não nas linhas) porque renderItems() esvazia a lista a cada mudança; miniaturas 140/85/50px, preferência em localStorage com fallback para large. 2a rodada (homologação): o item 3 encolhia só a MINIATURA e mantinha uma foto por linha — a caixa continuava ocupando a largura inteira, então trocar de modo quase não mudava a densidade. Refeito: flex-wrap transforma a lista em GRADE (caixa 1222x158 → 300x190 com 4 por linha → 147x97 com 8 por linha), controles do card em overlay no hover e legenda/link ocultos no menor. A ordem de leitura esquerda→direita cai naturalmente do DOM, então o onEnd do Sortable seguiu intacto — provado por arraste real e por interceptação do payload enviado ao servidor. Validação: 20 testes focados novos, Vitest 367/367, PHPUnit 784/784, resources:sync 0 erros e 19/19 + 12/12 + 2/2 checagens visuais autônomas com console limpo.

| BATCH-141 | implemented-pending-homologation | MIME real no admin-arquivos e correção da guarda de imagem no interface-v2 (req-138) | BATCH-141.md | O campo mime era $tipo . '/' . extensao — image/jpg (real image/jpeg), file/pdf (real application/pdf), file/json. Não quebrava porque todos os consumidores testam só o PREFIXO, mas o valor era exibido CRU ao usuário no picker. arquivo_mime_por_extensao() nasceu na biblioteca ao lado de arquivo_tipo_por_extensao(); um único ponto de origem alimenta os quatro caminhos de despacho do módulo. A invariante de prefixo virou TESTE sobre as 29 extensões reais, não comentário. Segundo defeito: dados.tipo?.match(/image/) === 'image/' no interface-v2 — match() devolve ARRAY, nunca string, então a guarda era SEMPRE falsa e o widget recusava 100% das imagens válidas. Validação: 14 testes focados novos, PHPUnit 784/784, Vitest 363/363, e runtime local confirmando application/json no lugar de file/json. | | BATCH-142 | implemented-pending-homologation | Refinamento de Densidade e Proporção no Galleries, Overlay de Configuração Rápida, Highlight de Cards e Controle de Alinhamento Vertical de Imagens (req-139) | BATCH-142.md | Grade desktop medida com 18 imagens: 6×110 px no médio e 10×65 px no pequeno, sem layout shift; overlay absoluto com highlight, backdrop e engrenagem; modal rápido persistindo legenda/link; image_position normalizado e aplicado aos 8 templates localizados e widget público. Validação: focados JS 7/7 + PHP 5/5 (74 asserções), Vitest 366/366, PHPUnit 789/789, recursos 2.678/0 erros e runtime autenticado sem console errors. |

| BATCH-143 | implemented-pending-homologation | 404 em imagens estáticas com hífen/espaço e colisão de upload sem espaço (req-140) | BATCH-143.md | Duas metades que se encaixavam: o desempate de colisão do upload gravava Ela (1).webp com espaço deliberado, e todo consumidor que publica a URL passa o caminho por arquivo_nome_sanitizar(), que troca espaço por hífen (regra do BATCH-100) — a página pedia Ela-(1).webp e o realpath() do controlador devolvia 404 de um arquivo que estava no disco. Módulo 2: arquivo_nome_colisao() nasceu na biblioteca ao lado de arquivo_nome_sanitizar() (precedente do BATCH-141) e passa o próprio resultado pela sanitização — sem isso, um nome-base terminado em hífen produziria base--(1), que a sanitização colapsa para base-(1) e reabre a divergência. Módulo 1: o intake sugeria trocar hífen por espaço, mas isso não alcança nome com os DOIS (Foto-Final de Praia.webp) e testar as combinações custaria 2^n acessos a disco numa string que o atacante controla. A correspondência é pelo RESULTADO da sanitização de cada entrada física, segmento a segmento — miniatura e diretório com espaço caem no mesmo mecanismo, sem código dedicado. Duas guardas por hífen evitam listar diretório nos 404 de varredura; o containment segue com arquivo_estatico_resolver_autorizado(). Medido no runtime local: /Ela-(1).webp e /mini/Ela-(1).webp 404 → 200, com ETag/304, Cache-Control e Range 206 preservados no caminho de fallback; upload real autenticado gravou Ela.webp → Ela-(1).webp → Ela-(2).webp. Validação: 19 testes focados novos (5 falham ao reintroduzir o comportamento do HEAD), PHPUnit 808/808, Vitest 366/366. | | BATCH-144 | ready-for-intake | Resolução de Conflitos e Paridade Visual das Três Camadas de Tailwind CSS entre Projeto, Templates (CDN) e Páginas Públicas (req-141) | BATCH-144.md | Herança dinâmica de CSS de templates em publicações no roteador público (gestor.php), extração de css_compiled em publisher-pages, persistência segura no admin-templates e blindagem da cascata contra bundles monolíticos. | | BATCH-144 | implemented-pending-homologation | Procedência do CSS derivado, CSS de conteúdo do Quill e auditoria/regeneração a partir do banco (req-141 / CR-002) | BATCH-144.md | O runtime serve TUDO do banco, mas o compilador varre arquivos de resources/ — e o editor online grava só no banco. O CSS entregue era compilado de um HTML que não é o entregue: medido 1.279 de 1.410 recursos Tailwind (91%) com ao menos uma classe sem regra nenhuma, e 85/182 classes sem CSS em /artigos/teste-de-pagina/. A pré-análise do intake apontava para injetar o CSS do template, mas o HTML da página JÁ contém as classes dele (o template é expandido no salvamento) — o que falta é o CSS da própria página. Quill: a página pública nunca carregou CSS do editor; .ql-indent-1 chegava sem regra e as classes que funcionavam vinham por acidente de um css_compiled contaminado em 2026-06 — daí o "tem hora que alinha". Resolvido com quill-content.css (134 regras da fonte oficial, sem !important, sem toolbar) injetado só onde há conteúdo Quill. Procedência: html/css são autoria e css_precompiled/css_compiled são derivados; a assinatura css_source_hash responde se o derivado corresponde à autoria vigente — provado nos três cenários (assinar → COERENTE; editar HTML → STALE; recompilar layout com HTML intacto → STALE, que era o caso sem sinal nenhum). Atomicidade: o deploy preservava html do usuário e deixava entrar o css_precompiled do sistema, criando um híbrido que ninguém compilou — era o perfil-usuario; agora a assinatura é invalidada sem apagar CSS. Ferramentas: c2f css:audit mede procedência e cobertura (só Tailwind — contar Fomantic inflava o número) e lista HTML/classe embutidos em PHP/JS; c2f css:rebuild compila do HTML do banco e, com --url, do HTML RENDERIZADO, fonte que dispensa declarar tailwind_sources. Medido: página 85 → 13 classes sem CSS (as 13 são prose-*, plugin typography ausente no projeto); lote de 80 páginas 1.538 → 291 (-81%); layouts stale 14 → 0. Dívida achada: perfil-usuario.js monta 26 classes em runtime — a razão das 40 declarações do módulo; o .php já está limpo. Validação: 46 testes novos, PHPUnit 855/855, Vitest 366/366. | | BATCH-145 | implemented-pending-homologation | Substituição do TinyMCE pelo Quill via a biblioteca editor-texto (req-142) | req-142.md | Feito: assets/interface/editor-texto.js (par JS da biblioteca PHP) faz a ponte com o formulário — o Quill edita uma <div>, mas quem o servidor recebe é o <textarea>, preservado, escondido e sincronizado a cada alteração E no submit. As 3 inicializações e os 3 pontos de leitura/escrita do configuracao.js migraram, a tag do [REDACTED] saiu e o html-editor-interface.js foi ajustado. Degradação pensada: sem o Quill o campo segue textarea editável. Verificado em runtime na tela variables/. 9 testes JS novos. PENDENTE: revogar a API key do TinyMCE no painel do fornecedor (segue no histórico do repositório). | O TinyMCE é licenciado e carrega do [REDACTED] com API key versionada no repositório. O núcleo são 11 arquivos: três new tinymce.Editor(...) em configuracao.js, a tag do CDN em configuracao.php:464, um setContent() no html-editor-interface.js e o markup/rótulos dos recursos. Com a biblioteca editor-texto do BATCH-144 a inclusão vira editor_texto_incluir(). Decidir se o tipo de variável tinymce é renomeado (com migração) ou mantido. | | BATCH-146 | implemented-pending-homologation | Assets de terceiros servidos do disco em vez de CDN (req-143) | VALIDATION-CHECKLIST.md#batch-146 | c2f assets:vendor baixa os 28 arquivos do registro com verificação de certificado SEMPRE ligada (o PHP CLI do Windows não tem CA bundle; a cadeia cai para o curl do sistema, nunca para VERIFYPEER => false). Inventario: jQuery em 4 pontos com 3 versões e 4 hosts; CodeMirror em 161 tags em 7 arquivos PHP; Quill preso em quill@2, uma FAIXA — e quill-content.css é gerado dela, entao a faixa permitia editor e página divergirem sozinhos. .gitignore ganhou exceção (!gestor/assets/vendor/): o vendor/ do Composer engolia os assets e a migração valeria só na máquina local. Medido: tela de variáveis com 26 assets do disco e 0 do CDN. PHPUnit 902/902, Vitest 378/378. Plano em BATCH-146.md. | | BATCH-147 | implemented-pending-homologation | Cadeia de recursos no galleries, saneamento da tela de variáveis e alvo do css:rebuild (req-144) | VALIDATION-CHECKLIST.md#batch-147 | A correção do BATCH-144 não chegava à tela: a galeria renderiza de uma cópia congelada do template em galleries.html (user_modified = 1 bloqueia o sync), e o template em uso é do PROJETO (galeria-home). Cadeia de três degraus, todos RECURSOS: galeria → template de origem → galleries-estados (novo, do core). O recurso que fornece as classes fornece também o CSS delas — sem isso .cursor-default ficava sem regra. Na tela variables, os 4 botões do scaffold agiam sobre modulos: excluir apagava o módulo inteiro; removidos. Tipo tinymce → editor-texto com migração Phinx e alias de leitura. project:update-all regenerava a base do SISTEMA reportando sucesso plausível. PHPUnit 902/902, Vitest 378/378. Plano em BATCH-147.md. | | BATCH-148 | implemented-pending-homologation | Fontes hospedadas pelo projeto e minificação de JavaScript no build (req-143, req-145) | VALIDATION-CHECKLIST.md#batch-148 | Abre corrigindo regressão do BATCH-146: o CSS do Fomantic pede as fontes por caminho RELATIVO, e migrar só o que aparece em <link>/<script> fez os ÍCONES DO GESTOR SUMIREM — daí a chave arquivos no registro. c2f assets:fonts traz o Google Fonts para dentro do projeto (28 faces, 50 descartadas por subset, unicode-range preservado); o @import que saiu do CSS de destaques ainda BLOQUEAVA a renderização. c2f assets:minify separa MINIFICAR (build, terser) de ESCOLHER (resolução), e por isso Content-Length, ETag, 304 e 206 seguem corretos — medido 17305 batendo byte a byte. 1.588 KB -> 739 KB (-53%). Zero requisição externa em todas as telas. PHPUnit 918/918, Vitest 378/378. Plano em BATCH-148.md. | | BATCH-149 | implemented-pending-homologation | Escopo do css:rebuild e assets locais no layout Tailwind | VALIDATION-CHECKLIST.md#batch-149 | A tela /perfil-usuario/ apareceu sem estilo: o css:rebuild que entrou no pipeline reescrevia 1.446 recursos para consertar 17, trocando o CSS do build offline (25.276 B, com tema, plugins e tailwind_sources) por um mais pobre (7.980 B). Duas causas: regenerarFontesDeclaradas() só lia <id>.json e o perfil-usuario declara as fontes no MANIFESTO DO MÓDULO; e o escopo nunca deveria ter sido o acervo inteiro — o híbrido do req-141 só existe onde alguém editou online. Escopo agora é user_modified=1 (--todos mantém o alcance). Junto: fomantic-icon (dist/components/icon.min.css, que meu inventário não viu) e lucide foram registrados e servidos do disco, com teste comparando a versão do layout com a do registro. Medido: CSS entregue 8.757 -> 26.053 B, classes sem regra 192 -> 96; sete telas com ZERO dependencia externa. PHPUnit 928/928, Vitest 378/378. | | BATCH-150 | implemented-pending-homologation | Modais de sistema como dependência automática das páginas Tailwind (req-148) | VALIDATION-CHECKLIST.md#batch-150 | O modal de alerta aparecia sem estilo na tela de login do Photon: botão com texto BRANCO sobre fundo TRANSPARENTE, 7 de 12 classes sem regra. O CSS existia (4.694 B); o que faltava era ele fazer parte da compilação DAQUELA página. interface_alerta() injeta o modal em qualquer tela, mas a declaração era manual — lista de EXCEÇÕES, e acessar-sistema estava fora. Os três modais viraram dependência automática no compilador, opcional porque exigi-los estritamente abortou a compilação do Photon (traz os componentes em pt-br, não em en); dependência DECLARADA segue falhando alto. Medido: 7 classes sem regra -> 0, bg-sky-600 de rgba(0,0,0,0) para oklch(...). PHPUnit 955/955, Vitest 378/378. | | BATCH-153 | implemented-pending-homologation | Poda e modularização dos READMEs e CHANGELOGs da raiz (req-151) | BATCH-153.md | Redução de 2.356 para 438 linhas (-81.4%) nos arquivos da raiz (README.md 91L, README-PT-BR.md 91L, CHANGELOG.md 128L, CHANGELOG-PT-BR.md 128L). Manuais detalhados migrados para ai-workspace/{en,pt-br}/docs/CONN2FLOW-{DEVELOPMENT-ENVIRONMENT,AMBIENTE-DESENVOLVIMENTO}.md. Releases legadas (v2.8.4 a v1.0.0) arquivadas em ai-workspace/{en,pt-br}/docs/changelogs/. Gates de release preservados (v2.10.0, instalador-v2.0.0, [2.10.0]). 24/24 links válidos. PHPUnit 965/965, Vitest 378/378. |

| BATCH-154 | implemented-pending-homologation | Paridade bare-metal no project:update-all (req-152) | BATCH-154.md | O estágio de banco exigia dockerPath e sempre usava docker exec, embora projetos HestiaCP tenham target host válido. Seleção determinística Docker/host, PHP [REDACTED] da raiz do Gestor, argumentos em array sem bash -c e bootstrap Phinx com $_GESTOR global. Lab [REDACTED]-lab: pipeline 5/5 aprovado duas vezes; repetição +0 ~0 =19. PHPUnit 1008/1008. | | BATCH-155 | implemented-pending-review | Transporte [REDACTED] pipeline de projeto e bootstrap CLI por host (req-153 / REQ-034) | BATCH-155.md | A migração para a VM Ubuntu + HestiaCP removeu o target/path_tests local que os três scripts do pipeline liam. Nova lib ai-workspace/en/scripts/lib/project-transport.sh resolve o destino de forma declarativa (deploy_mode: "ssh" + ssh_*), com recusa de caminho relativo ou /, BatchMode, citação printf %q do comando remoto e devolução de posse por chown. updates-manager-database.sh ganhou EXECUTION_MODE=ssh ao lado de docker e host. ProjectEnvironmentResolver publica deployMode/ssh; AuthCookieCommand gera sessão na VM por scp/ssh; CssRebuildCommand diagnostica o projeto remoto. config.php deixou de impor SERVER_NAME=localhost no ramo CLI — a imposição fazia o sufixo de cookie sair de md5('localhost') e o site ignorava a sessão em silêncio. req-153: checksums de admin-cron.json zerados, com ressalva de que o compilador os repreenche (ver seção 4 do batch). Validação: bash -n 4/4, novo ProjectSshDeployReq034Test 19/19, PHPUnit 1071/1071 (4.717 asserções, 0 falhas), project:update-all no Lab com etapas 1-5 em SUCCESS. | | BATCH-156 | implemented-pending-homologation | Auditoria, Normalização Visual e Reparo da Compilação de Tailwind CSS nos Templates de Páginas e Componentes (req-154) | BATCH-156.md | Sidecars e banco íntegros. Preview Tailwind isolado da folha Fomantic sem camada; baseline preservado ao inserir seções. A/B Chromium recuperou py-20 70→80px, gap-12 42→48px e fundo do CTA. 237/237 recursos recompilados; PHPUnit 1073/1073; Vitest 382/382; integridade 2/2 com 2.695 asserções. Homologação autenticada pendente. | | BATCH-157 | implemented-pending-review | Checksum idempotente e independente de plataforma no AdminCronReq032Test (REQ-035 / req-155) | BATCH-157.md | Duas causas, uma atrás da outra. (1) O teste exigia checksum VAZIO em admin-cron.json, na leitura de que um valor ali só podia ser manual — errado: atualizacao-dados-recursos.php grava o md5 do HTML de volta no manifesto (ORIGIN_UPDATE_MODULE), confirmando a ressalva do BATCH-155. (2) Corrigida a asserção, o CI da release v2.10.3 falhou com dois hashes concretos — e eles são do MESMO arquivo: git ls-files --eol → i/lf w/crlf, 387ee81b… é o md5 com CRLF da árvore Windows e 6a4af6df… o md5 com LF do runner Linux, 233 quebras de linha de diferença. Regravar o hash no Windows (o que a req-155 pedia) reproduziria o laço da req-153. Feito nos dois lados: manifesto recompilado da forma canônica (PaginasData.json perdeu o \r\n embutido, HTML idêntico após normalizar) e asserção passou a aceitar o md5 em qualquer das duas quebras — valor digitado à mão segue reprovado. Simulação do runner Linux 44/44; PHPUnit 1073/1073; Vitest 382/382. | | BATCH-158 | complete | Paridade visual estrita entre página pública, pré-visualizador e editor visual (req-158, cadastrada antes como req-156) | BATCH-158.md | O BATCH-156 tirou o semantic.min.css do PREVIEW e deixou a mesma tag, escrita à mão, nos dois ramos do EDITOR VISUAL — daí o "ambiente intermediário" do relato. São DUAS contaminações, e cada uma exige um mecanismo próprio: (1) folha sem camada vence utilities em @layer (título 72->24px, peso 900->700, texto do CTA branco->rgb(65,131,196)), resolvida por @import ... layer(c2f-editor-chrome); (2) html{font-size:14px} do Fomantic redefine a unidade rem e encolhe TODA medida do Tailwind por 14/16 = 0,875 (128->112px, 48->42px) — nenhuma camada corrige, porque não há regra do Tailwind disputando html{font-size}; exige restaurar a raiz. Só com a camada, a paridade parava em 63px. A folha não podia sair como no preview: #html-editor-modal é um modal Fomantic. A divergência da rota pública NÃO se reproduziu no ambiente real — o css_compiled do Tailwind v3 está no espelho do banco no container; a VM que serve [REDACTED] já entrega v4 com @layer e zero CDNs. A guarda de procedência é preventiva e fecha uma assimetria real: o build offline sempre carimbou a versão do Tailwind no fingerprint, o derivado gravado ONLINE não — assinatura passou a v2: com compilador, e toda v1: entra na fila do css:rebuild. Junto, por decisão do operador: os iframes, a Editbar e 5 previews de widget montam tags no CLIENTE e escaparam do inventário do BATCH-146 — 24 tags em 4 CDNs migradas para o registro (DEC-122), incluindo @tailwindcss/browser, que nunca esteve nele e tinha a versão repetida em 7 lugares, e os addons closetag/closebrackets, usados só pelo editor e ausentes do registro. Medido contra a rota pública REAL: Editor x Preview 15/15 e Pública x Preview 15/15 (antes, 15/15 divergentes). Zero CDNs nos autorais e nos .min.js. Vitest 393/393; PHPUnit 1.092/1.092 (7.523 asserções). | | BATCH-159 | implemented-pending-review | Eliminar recompilação redundante de recursos Tailwind no release do Gestor (req-156) | BATCH-159.md | Removidas a recompilação e o commit remoto; o job empacota diretamente os derivados pré-compilados e testados pelo release local. YAML, PHPUnit 1073/1073 e Vitest 382/382 aprovados. | | BATCH-160 | complete | Animações de entrada dos templates de sessão (req-159) | BATCH-160.md | Auditoria da pasta de templates pedida pelo operador ao fechar o BATCH-158. Quatro templates aplicavam animate-fade-in-up sem que o token existisse em contrato nenhum — no Tailwind v4 a classe animate-<nome> só nasce com --animate-<nome> no @theme, e sem ele a utility é descartada em SILÊNCIO. Medido em Chromium: animation-name: none, duração 0s. O contraste deu a causa: sessao-com-abas anima porque traz o @keyframes num <style> embutido; os quatro afetados não trazem nada. animate-pulse (38 usos) e animate-bounce (26) funcionam por serem nativas. Severidade baixa pelo motivo certo: nenhum elemento afetado tem opacity-0 junto, então todos aparecem e apenas entram estáticos — fosse o contrário, ficariam invisíveis para sempre. Corrigido no contrato central (system-input.css) por decisão do operador, e não em <style> por template: tailwind_recursos_browser_contract() DERIVA o browser-contract.css dele, então a definição alcança build offline, editor visual e Editbar de uma vez. Medido: 14/14 animações vivas (era 0/14), sem regressão nas nativas; resources:sync --force 237/237; guarda validada por mutação (removida a regra, 2 testes falham nomeando template e classe). PHPUnit 1.096/1.096 (7.547 asserções); Vitest 394/394. Ressalva: o input do PROJETO substitui o do core (não estende), então projeto com contrato próprio não herda a animação — mudar isso exige change request. | | BATCH-161 | complete | O CSS dos templates inseridos não chegava ao runtime público (req-160) | BATCH-161.md | O BATCH-158 igualou os três ambientes com o conteúdo já estabilizado; este defeito nasce na TRANSIÇÃO de inserir um template, e por isso passou. Cadeia medida atravessando os dois repositórios de semente: .border-b-2 existe no sidecar do template (core), não existe no sidecar do layout (projeto) nem no css_compiled (banco) — a regra existe na origem e some no runtime. Cobertura: publicada 424 classes/13 sem regra, preview 405/5; as 8 de diferença são todas de sidecar de template. Causa: o BATCH-156 passou a somar o sidecar ao baseline (para o editor renderizar certo) e com isso ele entrou no conjunto contra o qual HtmlEditorCssCapture FILTRA — as regras do template deixaram de ser gravadas, e o runtime, que só recebe layout.css_precompiled + css_compiled, ficava sem elas. Primeira tentativa descartada: persistir o baseline em paginas.css_precompiled foi reprovado por CssProcedenciaGravacaoTest — "derivado gravado do POST devolveria ao operador a capacidade de descolar CSS de HTML" (CR-002). A guarda estava certa; a correção foi refeita sem tocar no contrato. Solução: o baseline passa a ser emitido em DUAS folhas — baseline (o que o runtime entrega, alvo do filtro) e session-overlay (o sidecar da sessão, fora do filtro). Mesma cascata no iframe, e a captura volta a gravar o que falta. Uma mudança, num arquivo; nenhum módulo alterado. Guarda validada por mutação. PHPUnit 1.096/1.096. Na homologação apareceram mais três causas, independentes da primeira: (1) HtmlEditorCssCapture só era injetado no iframe do EDITOR VISUAL — no preview a captura falhava sempre com sem-motor e preservava o valor anterior, vazio numa página nova; quem montava pelo adicionar/, conferia no preview e salvava gravava css_compiled VAZIO (medido: 360 classes aplicadas, 0 byte). Motor injetado no preview com __c2fHtmlEditorNoAutoInit: 0 -> 38.990 bytes. (2) nada garantia a geração antes do save — por proposta do operador, o editor agora GERA e então envia, aguardando a captura AVISAR que terminou (aoConcluir, sem tempo fixo), só quando o HTML mudou de fato e sem travar página sem classe Tailwind; validado: 38.990 -> 39.020 cobrindo a classe nova antes do envio. (3) o CSS AUTORAL do layout (coluna css) nunca era lido — body{background:#000} pintava a publicada e não o editor, o sintoma relatado desde o início; emitido como layout-authored fora do baseline, com rota html-editor-layout-css para a TROCA de layout no select. Ciclo no Lab: publicada e preview com 5 classes sem regra cada (era 99 x 5) e mesmo fundo; galeria masonry com 0 (columns-* e break-inside-avoid eram as ausentes). Vitest 408/408. Homologado pelo operador. | | BATCH-162 | complete | Eliminação de requisição espúria 404 de {{thumbnail}} no carregamento do Editor HTML via <template> nativo (req-161) | BATCH-162.md | O molde de card de modelo em html-editor-modelos (pt-br e en) usava <div> comum, fazendo o parser HTML do navegador instanciar <img> e tentar baixar {{thumbnail}} no carregamento, resultando em 302 para 404. Migrar para <template id="modelo-card-template">, mantendo compatibilidade com $('#modelo-card-template').html(), sincronizar recursos e adicionar guarda automatizada. | | BATCH-164 | implemented-pending-homologation | Renderização de variáveis e prevenção de 404 no editor visual de layouts (req-162) | BATCH-164.md | Layouts passam pelo renderizador de variáveis antes do srcdoc; atributos resolvidos guardam original e valor renderizado para restauração condicional no save; a extração usa <template> inerte para não disparar rede. Recursos 2.844/0 erros, Vitest 411/411 e PHPUnit 1.114/1.114. |

| BATCH-169 | complete | Blindagem de rsync contra MSYS Path Conversion no transporte [REDACTED] Windows (req-164) | BATCH-169.md | Helper compartilhado executa rsync com MSYS_NO_PATHCONV=1; três chamadas do sync de core e o executor comum do sync de projeto ficaram protegidos. Skill replicada nos cinco espelhos. bash -n 3/3; teste focado 23/23; PHPUnit 1.175/1.175; Vitest 423/423. Homologação [REDACTED] no Lab pendente. | | BATCH-170 | complete | Sanitização ANSI no detector de versão do Tailwind CLI e governança de terminal rsync Windows (req-165) | BATCH-170.md | Saída do CLI é sanitizada antes da regex; testes cobrem Linux e ANSI do Windows. Manifesto estabilizado em 4.3.3; repetição do resources:sync confirmou 0 para compilar, 237 em cache. Skills idênticas nos cinco espelhos. PHPUnit 1.178/1.178 (7.801 asserções). | | BATCH-171 | complete | Pareamento cwRsync/Cygwin com [REDACTED]ível no Windows (req-166) | BATCH-171.md | O code 12 vinha da mistura cwRsync/Cygwin + [REDACTED] Git/MSYS2. Transporte seleciona o [REDACTED] próprio cwRsync, preserva MSYS_NO_PATHCONV=1 e traduz somente origens locais /c/... para /cygdrive/c/.... Homologação real project:sync-core [REDACTED]-local exit 0; teste focado 24/24; PHPUnit 1.179/1.179. | | BATCH-172 | complete | Elevação do diretório de trabalho no transporte [REDACTED] tenants HestiaCP (req-167) | BATCH-172.md | project_transport_remote_exec passa cd e comando juntos para sudo -u <tenant> sh -c, preservando execução direta sem ssh_run_as. Homologação real project:sync-db [REDACTED]-local exit 0, sem Permission denied; teste focado 25/25; PHPUnit 1.180/1.180; skills com MD5 idêntico. | | BATCH-173 | complete | Paridade de elevação do diretório nos executores [REDACTED] PHP (req-168) | BATCH-173.md | SshRemoteTransport e AuthCookieCommand passam cd e comando juntos para sudo -u <tenant> sh -c, preservando o caminho direto sem runAs. project:update-all [REDACTED]-local --confirmar-remoto concluiu 1/8–8/8, com etapa 6/8 sem Permission denied; testes focados 23/23; PHPUnit 1.181/1.181. | | BATCH-174 | complete | Controle de acessos — separar erro de validação de abuso e informar prazo de liberação (req-169) | BATCH-174.md | formulario_acesso_falha() ganhou origem (validacao vs abuso), evitando que erro de digitação esgote cota de envios. Telas de bloqueio informam data/hora exata de liberação. Defaults atualizados no .env. PHPUnit 1.181/1.181, homologação real aprovada. | | BATCH-175 | complete | Idempotência e isolamento no teste ForcarAtualizacaoTest (req-170) | BATCH-175.md | ForcarAtualizacaoTest tornado idempotente, eliminando falhas fantasmas intermitentes em suítes sequenciais. PHPUnit 1.181/1.181 verde. | | BATCH-176 | complete | Suporte nativo ao Cloudflare Turnstile no Core do Conn2Flow (req-171) | BATCH-176.md | Provedor configurável, Siteverify centralizado, integração em autenticação e formulários, configuração e teste no admin-environment. PHPUnit 1.187/1.187; Vitest 423/423; pipeline SSH/HestiaCP concluído no Lab. SQL e Playwright confirmaram quatro rotas e painel; teste AJAX e salvamento aprovados com chaves oficiais, .env original restaurado. | | BATCH-177 | complete | Correção do Fluxo de reCAPTCHA v3/v2 e Turnstile em Telas de Autenticação Tailwind (req-172) | BATCH-177.md | Captura de tokens desacoplada do Fomantic, fallback v3 para v2 validado e suítes PHPUnit/Vitest verdes; fluxo publicado e inspecionado em [REDACTED]. | | BATCH-178 | complete | Preservação de Query Strings em Redirecionamentos 301 e Contenção de Blocos-Fragmento no Widget de Formulários (req-173) | BATCH-178.md | Passar querystring => true no 301 de paginas_301 e tratar concatenação segura em gestor_redirecionar; expurgar blocos-fragmento (option-choice, option-select, password-toggle) e sanitizar marcadores no widget de formulários. Testes 12/12 focados, PHPUnit 1.202/1.202 e validação no Lab. |

O Futuro é Híbrido: Humano + IA.

O Conn2Flow é a ferramenta. Se você precisa do Arquiteto para desenhar a estratégia da sua empresa usando essa tecnologia...

Contrate Consultoria Especializada