Conn2Flow
Navegação das docs

SDD

Decision Log (Archive: DEC-061 to DEC-070)

Refatoração dinâmica do módulo Forms com html-editor (req-049 / BATCH-049). Decisões desta rodada: 1. Forms passa a seguir o padrão dos widgets visuais: a tabela forms recebe…

DEC-061 - 2026-06-22 - accepted

Refatoração dinâmica do módulo Forms com html-editor (req-049 / BATCH-049). Decisões desta rodada:

  1. Forms passa a seguir o padrão dos widgets visuais: a tabela forms recebe html, css, css_compiled e html_extra_head; o CRUD administrativo usa html_editor_componente com alvo forms; e o template selecionado é preservado com a opção -modificado quando há código customizado no banco.

  2. fields_schema permanece como contrato estruturado único: metadados gerais, e-mail, redirects, limites, reCAPTCHA e fields[] são serializados pelo JS em fields_schema, preservando retrocompatibilidade com a coluna JSON existente e evitando criar tabelas auxiliares neste slice.

  3. Renderização pública baseada em loop de campos: forms.widget.php processa o bloco <!-- item < --> ... <!-- item > -->, resolve [[item#label]], [[item#name]], [[item#placeholder]], [[item#type]], [[item#required]], [[item#options]] e aplica blocos condicionais type-input, type-textarea e type-select. Recursos de CSS/head usam gestor_pagina_recursos_incluir(), mantendo o HTML retornado limpo.

  4. Parâmetro público preferencial form_id: o wrapper de widget usa forms->render({"form_id":"slug"}), mas o renderer aceita também grupo_slug/id como fallback para compatibilidade com mecanismos genéricos.

DEC-062 - 2026-06-22 - accepted

Correções do visual editor, colisão de abas e novos templates para Forms (req-050 / BATCH-050). Decisões desta rodada:

  1. Parâmetro de escape HTML na renderização do editor: Introduzir o parâmetro opcional html_specialchars no html_editor_componente(). Quando habilitado, o backend escapará os placeholders #pagina-html# e #pagina-html-extra-head# via htmlspecialchars() para evitar o encerramento precoce de tags <textarea> no HTML original. O CodeMirror decodificará as entidades automaticamente no DOM ao ler a propriedade .value do <textarea>.

  2. Isolamento de abas aninhadas via context no JS: Corrigir o bug que faz os menus de abas desaparecerem ao clicar na sub-aba "Widget". Isolar o escopo do menu externo menuForms usando context: 'parent' e o escopo do menu interno menuFormsTemplate usando context: '[data-tab="forms-template"]', impedindo que a ativação da aba "Widget" oculte a aba pai "Template".

  3. Criação de novos templates baseados em Tailwind CSS: Desenvolver 4 novos modelos de formulário (Newsletter, Registro de Usuário, Pesquisa de Satisfação e Suporte Técnico) duplicados em português e inglês (totalizando 8 novos registros), todos utilizando Tailwind CSS e a mesma estrutura dinâmica de repetição do widget (<!-- item < -->).

  4. Execução local do compilador de recursos: Após registrar os templates físicos no manifest, executar atualizacao-dados-recursos.php para recalcular checksums e dados derivados, mantendo o padrão do projeto de não editar checksums manualmente.

DEC-063 - 2026-06-22 - accepted

Escape HTML global no editor e separador de valor/rótulo nos selects do Forms (req-051 / BATCH-051). Decisões desta rodada:

  1. Comportamento global de escape HTML no editor: Tornar o escape de tags HTML e HTML Extra Head o padrão no html_editor_componente() se $html ou $html_extra_head forem informados no array $params, removendo a necessidade do parâmetro explícito html_specialchars.

  2. Limpeza do parâmetro no módulo Forms: Remover a linha 'html_specialchars' => true da chamada em forms.php, já que a passagem de html/html_extra_head no $params passará a ativar o escape por padrão.

  3. Mapeamento de Valor e Rótulo nos Selects: Atualizar forms_widget_options_html() no backend para interpretar a sintaxe de divisão de valor e rótulo em strings de opção (usando os caracteres separadores | e :). Se contiver o separador, a parte esquerda vira value e a direita vira label. Caso contrário, mantém-se o mesmo comportamento de usar o valor total para ambos.

  4. Radio e checkbox compartilham o contrato de opções: Reaproveitar options e os blocos condicionais do template para renderizar radio e checkbox, evitando criar uma segunda estrutura de schema. No HTML público, checkbox usa name[] para permitir múltiplos valores.

  5. CRUD mostra opções só para tipos com lista: A textarea de opções no administrativo fica visível apenas para select, radio e checkbox; ao trocar para tipo simples, o valor é limpo para evitar que opções antigas fiquem persistidas no schema.

DEC-064 - 2026-06-23 - accepted

Sincronização Declarativa de Recursos, Deleção e Atualização Forçada — Módulos e Globais (req-056 / BATCH-056). Decisões desta rodada:

  1. config como objeto OU array de objetos: normalizarConfigTabela() passa a retornar uma LISTA de configurações normalizadas; um objeto único é convertido internamente em array de 1 elemento. Cada elemento pode nomear sua tabela via tabela_nome (default = tabela.nome do bloco), permitindo que um único bloco tabela.config declare N tabelas. As listas deletar/forcar_atualizacao podem vir dentro de cada elemento de config ou no nível do bloco (este último herdado apenas quando a tabela coincide com o nome do bloco — retrocompat com BATCH-029).

  2. Coleta unificada (coletarConfigsTabelas): novo motor compartilhado por gerarSchemaMetadata() (contrato) e por coletarRecursos() (varredura dinâmica), mantendo a ordem determinística núcleo→global→módulos e o contexto de varredura (scope/modulo/base_dir/inline). O contrato schema-metadata.json ganha o mapa de topo forcar_atualizacao (espelhando deletar), consolidado/agregado por tabela; os campos de varredura/diretivas de recurso NÃO vão ao contrato (são build-time).

  3. Varredura declarativa de recursos (sync_resources): tabelas com sync_resources:true geram <PascalCase>Data.json automaticamente. Metadados vêm de arquivo externo (metadata_file) ou inline (resources -> idioma -> tabela). field_types converte campos: json (json_encode) e file:<ext> (injeta <id>.<ext> sem BOM). Cada registro recebe language/status='A'/versao/user_modified=0 + checksum MD5 (sobre arquivos file:* ou payload) para reuso de versão entre deploys. Tabelas reservadas (as 9 fixas do pipeline) são puladas para não colidir com a geração existente.

  4. Atualização forçada no atualizador (forcar_atualizacao): em sincronizarTabela(), registros que casam por pk ou natural_key ignoram a proteção de project e a preservação de user_modified (aplicam payload completo), resetam user_modified=0 quando era 1, e preservam o valor de project (não alteram/limpam). Aplicado nos três caminhos de update (PK, chave natural e fallback de linguagem ausente).

  5. tables_config.json global apenas documentado: o arquivo global manteve as 4 tabelas existentes (config objeto, retrocompat) e teve o _comment estendido para descrever config objeto/array, tabela_nome e os novos parâmetros — sem fabricar tabela nova (não havia requisito de produto para isso).

  6. Cobertura de teste: novo tests/Unit/PHP/ForcarAtualizacaoTest.php (PDO SQLite + contrato temporário) blinda os 4 cenários (forçado por chave natural, forçado por PK, preservação normal, proteção de projeto). Correção colateral: interpolação ${var} obsoleta (PHP 8.4) em atualizacoes-banco-de-dados.php trocada por {$var} (exposta pela inclusão do arquivo no PHPUnit).

DEC-065 - 2026-06-24 - accepted

Correção de Tipagem e Validação de Perfil Anônimo em Menus Condicionais (req-057 / BATCH-057). Decisões desta rodada:

  1. Normalização de Usuários Anônimos em Array: Normalizar $perfil_usuario para false quando este for fornecido como um array cuja chave id é '_anonimo' ou cuja chave id_usuarios resolva para 0. Isso garante o alinhamento com a lógica que espera false para usuários não autenticados e resolve falhas de asserção em testes de visibilidade pública.

  2. Prevenção de Warnings de Conversão: Evitar a conversão implícita de array para string ((string)$perfil_usuario) na resolução do $user_profile_id quando $perfil_usuario for um array. O identificador correto deve ser extraído de $params['_profile_slug'] (caso exista) ou da chave id_usuarios_perfis do próprio array de perfil, eliminando os warnings do PHPUnit e permitindo o correto casamento de condições de perfis com múltiplos IDs permitidos.

DEC-066 - 2026-06-25 - accepted

Sistema de Recuperação e Engenharia Reversa de Recursos (Pull System) (req-058 / BATCH-058). Decisões desta rodada:

  1. Reuso da exportação reversa existente, não recriação: a função reverseExport($pdo, $tabelas, $dataDir) (e os helpers db()/schemaMetadata()/tabelaFromDataFile()) já existiam em atualizacoes-banco-de-dados.php (modo --reverse). O endpoint _api/project/recover e o controlador servidor apenas as orquestram, incluindo o atualizador com define('SDD_NO_AUTORUN', true) para reaproveitar as funções sem disparar o deploy. O endpoint gera o dump bruto num diretório temporário, compacta os *Data.json via ZipArchive e faz stream com Content-Type: application/zip, limpando o disco em seguida.

  2. Layout físico PLANO no descompilador (simetria com o compilador): os arquivos file:<ext> são gravados em <base_dir>/<lang>/<resources_dir|tabela>/<id>.<ext> (sem subpasta por id) e os metadados externos sem resources_dir ficam em <lang>/<metadata_file>. A prosa do intake §2.1 sugeria <id>/<id>.<ext>, mas isso quebraria o round-trip — o compilador (processarRegistroDinamico/lerMetadadosDinamicos) lê o layout plano. Seguiu-se o código (mesmo precedente do BATCH-056). O recover-project.sh recompila os recursos após a descompilação justamente para garantir consistência total dos *Data.json.

  3. Saneamento simétrico ao compilador: o descompilador é o reverso exato das injeções do build — decodifica campos json (string→array), extrai file:<ext> para arquivo (BOM removido) e remove as colunas geradas: versao, checksum, user_modified, project, a PK auto-increment (id_numerico do contrato) e a coluna de idioma (language/linguagem_codigo, derivada da pasta); remove status quando 'A' (default injetado) e module/modulo quando igual ao módulo dono e presente na chave natural. Metadados externos (metadata_file) ou inline (resources->idioma->tabela) são reescritos preservando o restante do JSON raiz.

  4. Descompilador autônomo e --project-path: o descompilador faz sua própria varredura de tables_config.json + modulos/*/<mod>.json (em vez de incluir o compilador), mantendo-o testável isoladamente e blindado por SDD_NO_AUTORUN. Tanto o descompilador quanto o recover-project.sh aceitam --project-path, alinhando-se ao update-resource-data.sh/deploy (operação por projeto) — uma extensão sobre o exemplo literal do intake §3.1, que não passava o caminho do projeto. Metadados de recurso são gravados com JSON_UNESCAPED_SLASHES para legibilidade no Git.

  5. Cobertura de teste: novo tests/Unit/PHP/RecuperacaoDadosRecursosTest.php (6 testes, 44 assertions) blinda extração de arquivos físicos, decodificação de JSON, saneamento de colunas, escrita externa/inline de metadados e a resolução de caminhos (global com/sem resources_dir e módulo), todos com fixtures em diretório temporário, sem banco.

DEC-067 - 2026-06-25 - accepted

Atualização de Versões dos Componentes do GitHub Actions (req-064 / BATCH-064). Decisões desta rodada:

  1. Atualização de Versões de Actions: Atualizar as ações nos workflows para as últimas versões estáveis disponíveis que oferecem suporte nativo ao runtime Node.js 24: actions/checkout@v4 → actions/checkout@v7, actions/setup-node@v4 → actions/setup-node@v6 e softprops/action-gh-release@v2 → softprops/action-gh-release@v3.

  2. Mitigação de Depreciação: Isso remove completamente os avisos de deprecamento do Node.js 20 sem alterar o fluxo funcional ou as tarefas executadas nos scripts dos pipelines.

DEC-068 - 2026-06-29 - accepted

Registro Dinâmico de Widgets do Sistema e Carregamento sob Demanda no Editor HTML (req-066 / BATCH-066). Decisões desta rodada:

  1. Tabela global widgets populada pela esteira de recursos: nova tabela widgets (id/name/icon/tabela/coluna_where/language/status/versao/project, índice único [id, language]) descreve cada categoria/tipo de widget do sistema, substituindo o mapa estático de tabelas tanto no backend quanto no frontend. A coluna project nasce junto (espelha 20260707100000_add_project_to_widget_tables.php) para não quebrar o UPSERT de deploy de projeto.

  2. Geração via caminho CLÁSSICO do compilador, não pelo dinâmico sync_resources: o recurso "widgets" é declarado em resources->idioma->widgets nos manifestos dos módulos e coletado pela varredura fixa de coletarRecursos() (pilha $widgets, índice $idxWidgets por lang|id, órfãos em db/orphans/WidgetsData.json), gerando db/data/WidgetsData.json. A tabela é declarada em tables_config.json apenas para entrar no contrato schema-metadata.json (strategy natural_key por [language, id]) consumido pelo atualizador — sem sync_resources (evita dupla geração; também adicionada à lista $reservadas do caminho dinâmico por segurança). O Data.json usa a chave name (não nome) para casar com a coluna real da tabela; versao = 1 fixo (registro estrutural sem arquivos físicos).

  3. Carregamento sob demanda (lazy loading) por categoria no frontend: o painel de inclusão (html-editor-visual-controls.js) deixa de ter WIDGETS_MODULOS hardcoded. Ao abrir, busca as categorias ativas via nova rota html-editor-widget-types e renderiza os grupos colapsados; ao expandir um grupo, busca os registros daquela categoria via html-editor-widgets-list (parâmetro module), com cache local por módulo (widgetsCache[module]) para evitar chamadas redundantes.

  4. Resolução dinâmica e segura da tabela alvo no backend: html_editor_ajax_widgets_list() passa a receber module, resolve tabela/coluna_where na tabela widgets e consulta dinamicamente a tabela alvo. Os identificadores tabela/coluna_where são validados por regex ^[a-zA-Z0-9_]+$ antes da interpolação (anti-SQL Injection); a coluna de rótulo padrão é name, com fallback para id quando ausente/vazia.

DEC-069 - 2026-06-29 - accepted

Correção de Exceção de CodeMirror Indefinido no Módulo de Formulários (req-067 / BATCH-067). Decisões desta rodada:

  1. Guard de existência antes de manipular instâncias CodeMirror: como a dependência da biblioteca html-editor no forms.json faz html-editor-interface.js carregar em todas as telas do módulo (inclusive listar/visualizar, que não renderizam o editor), o registro do listener CodeMirrorHtml.on("change", ...) — que executa no $(document).ready — quebrava a página com TypeError: Cannot read properties of undefined (reading 'on'). A correção envolve esse registro e o handler de backup ($('#gestor-listener').on(backupCallbackName, ...), casos html/css/css_compiled) com a verificação typeof X !== 'undefined' && X, padrão já usado para CodeMirrorHtmlExtraHead. Escopo deliberadamente cirúrgico (apenas os pontos que rodam/disparam sem o editor montado), sem refatorar os demais usos protegidos pelo fluxo do editor ativo.

DEC-070 - 2026-06-29 - accepted

Correção de Causa-Raiz das Abas Travadas e Widget Vazio no Módulo de Formulários (req-067 expandido / BATCH-067, rodada 2). Após os findings expandidos do Engenheiro Chefe (erros em forms/editar/ e forms/adicionar/, onde o editor DEVERIA existir), decisões desta rodada:

  1. Editor HTML fora do aninhamento de abas (causa-raiz): no forms, o #html-editor# estava aninhado em abas inativas no load (forms-template → forms-preview/forms-editor), enquanto módulos funcionais como publisher-highlights o renderizam direto na página. O CodeMirror (CodeMirror.fromTextArea no ready) e a inicialização das abas internas do editor não se estabilizavam dentro do contexto de abas externas ocultas, deixando CodeMirrorHtml/CodeMirrorCss indefinidos e travando a troca de abas Fomantic (exceções não capturadas em previewHtml/totalDeSessoes). Decisão (orientação do Engenheiro Chefe Humano): remover a sub-aba "Editor HTML" do menuFormsTemplate e mover o #html-editor# para uma seção raiz <h4 class="ui dividing header">Conteúdo do Formulário</h4> logo abaixo das abas, sempre visível, nos 6 HTMLs (editar/adicionar/clonar × pt-br/en; "Form Content" no en).

  2. Blindagem defensiva ampliada: além dos guards da rodada 1, proteger previewHtml(), getUpdatedHtmlWithValues(), totalDeSessoes() e menuDeSessoes() com early-return quando as instâncias CodeMirror não existem — defesa em profundidade que não altera o fluxo com o editor montado.

  3. Fallback de template no widget público (forms.widget.php): forms_render() retornava '' quando o campo html do registro estava vazio. Esse vazio era consequência do mesmo bug — sem o CodeMirror instanciado, o HTML do template não era carregado/sincronizado no submit, gravando html vazio no banco. Decisão: quando html estiver vazio, carregar html/css/css_compiled/html_extra_head do template referenciado em fields_schema.template_id (tabela templates, target='forms'), resolvendo registros legados e dando robustez. Difere de D-023 (que veda fallback para o mockup de arquivo físico): aqui o fallback é para o template do próprio banco.

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