Translate

domingo, 5 de agosto de 2018

Blog Analytics : Parte VI – Criando o Motor de Regras do Bellacosa Blog Doctor

 

Bellacosa Mainframe e o blog analytics parte vi

☕ Um Café no Bellacosa Mainframe

Blog Analytics : Parte VI – Criando o Motor de Regras do Bellacosa Blog Doctor

Como transformar sinais dispersos em diagnósticos padronizados, prioridades, códigos de retorno e recomendações automáticas

Quando um simples if deixa de ser suficiente e nasce o RACF das regras editoriais

☕ Um Café no Bellacosa Mainframe

Na Parte V, construímos a arquitetura geral do Bellacosa Blog Doctor.

Definimos a ferramenta como um scanner automático capaz de ler o Feed Atom do Blogger, normalizar milhares de publicações, analisar HTML, SEO, acessibilidade, conteúdo, links, imagens e resíduos deixados por editores externos.

Mas uma arquitetura, por melhor que seja, ainda é apenas o mapa do Data Center.

Ela mostra:

  • onde os dados entram;

  • onde são processados;

  • onde são classificados;

  • onde os relatórios serão gerados.

Falta agora construir aquilo que realmente decide se um post está saudável, suspeito, degradado ou perigosamente próximo de um ABEND editorial.

Falta o Motor de Regras.

É esse componente que transforma observações técnicas em diagnósticos consistentes.

Sem ele, teríamos apenas dezenas de funções independentes:

if (tituloCurto) ...
if (imagemSemAlt) ...
if (possuiResiduoIA) ...
if (naoTemLinkInterno) ...
if (temTabelaDentroDeParagrafo) ...

No começo, isso parece funcionar.

Depois surgem vinte regras.

Em seguida cinquenta.

Logo aparecem exceções, categorias, pesos diferentes, severidades, mensagens, sugestões de correção, limites configuráveis e versões históricas.

Quando percebemos, o código virou um emaranhado de decisões espalhadas por toda a aplicação.

No mainframe, isso seria como esconder regras críticas de negócio em centenas de parágrafos COBOL, sem tabela de decisão, sem copybook, sem documentação e sem qualquer possibilidade de saber qual alteração afetou qual processamento.

O Bellacosa Blog Doctor precisa de algo melhor.

Precisa de um motor centralizado, extensível, auditável e previsível.


O Que é um Motor de Regras?

Um motor de regras é um componente responsável por receber dados, aplicar condições predefinidas e produzir resultados padronizados.

No nosso caso, ele recebe um objeto representando um post:

{
  titulo: "ABEND sem Mistérios",
  palavras: 1842,
  html: "<h2>...</h2>",
  imagens: 4,
  imagensSemAlt: 2,
  linksInternos: 0,
  marcadores: ["COBOL", "Mainframe"],
  residuosIA: 3
}

Depois percorre um catálogo de regras.

Cada regra responde perguntas como:

  • este post viola alguma condição?

  • qual é a gravidade?

  • quantos pontos devem ser descontados?

  • qual mensagem deve ser apresentada?

  • qual recomendação deve ser exibida?

  • qual código de diagnóstico será associado?

  • a regra deve ser ignorada em algum contexto?

O resultado deixa de ser apenas um booleano.

Passa a ser um diagnóstico completo.

{
  codigo: "BBDACC002E",
  categoria: "Acessibilidade",
  gravidade: "alta",
  quantidade: 2,
  penalidade: 8,
  mensagem: "Foram encontradas imagens sem texto alternativo",
  recomendacao: "Adicionar atributos ALT descritivos às imagens"
}

Esse formato é o equivalente editorial de uma mensagem padronizada de sistema.


Por Que Não Espalhar if Pelo Código?

Imagine uma versão inicial.

let score = 100;

if (post.titulo.length < 20) {
  score -= 7;
}

if (post.palavras < 300) {
  score -= 15;
}

if (post.imagensSemAlt > 0) {
  score -= 10;
}

Esse código não está errado.

O problema aparece quando o projeto cresce.

Suponha que cada regra também precise ter:

  • código;

  • categoria;

  • mensagem;

  • recomendação;

  • gravidade;

  • penalidade máxima;

  • quantidade;

  • exceção;

  • versão;

  • documentação.

O if simples já não é suficiente.

Pior: se as regras estiverem espalhadas em funções diferentes, torna-se difícil responder:

  • quantas regras existem?

  • quais estão ativas?

  • quais pertencem ao módulo SEO?

  • quais são críticas?

  • qual regra gerou determinado alerta?

  • qual versão introduziu a penalidade?

  • quais regras devem ser desativadas em posts antigos?

Em sistemas críticos, regra espalhada é dívida técnica.

O Motor de Regras existe para concentrar as decisões.


A Estrutura Básica de uma Regra

Uma regra deve ser tratada como um objeto configurável.

const regraTituloCurto = {
  codigo: "BBDSEO001W",
  nome: "Título muito curto",
  categoria: "SEO",
  gravidade: "media",
  penalidade: 7,
  testar(post) {
    return post.titulo.length < 20;
  },
  mensagem(post) {
    return `O título possui apenas ${post.titulo.length} caracteres`;
  },
  recomendacao:
    "Revisar o título e torná-lo mais descritivo sem exagerar no comprimento"
};

Essa estrutura já é muito mais poderosa.

Ela separa:

  • identidade;

  • lógica;

  • gravidade;

  • penalidade;

  • explicação;

  • recomendação.

O motor não precisa saber os detalhes de cada regra.

Ele apenas executa todas de forma padronizada.


O Catálogo Central de Regras

O ideal é guardar as regras em um único catálogo.

const regras = [
  regraTituloCurto,
  regraTituloLongo,
  regraConteudoCurto,
  regraImagemSemAlt,
  regraSemLinkInterno,
  regraResiduoIA,
  regraResiduoWord,
  regraTabelaEmParagrafo
];

Ou diretamente:

const regras = [
  {
    codigo: "BBDSEO001W",
    categoria: "SEO",
    gravidade: "media",
    penalidade: 7,
    testar: post => post.titulo.length < 20,
    mensagem: post =>
      `Título com ${post.titulo.length} caracteres`,
    recomendacao:
      "Criar um título mais informativo"
  },
  {
    codigo: "BBDACC002E",
    categoria: "Acessibilidade",
    gravidade: "alta",
    penalidade: 4,
    penalidadeMaxima: 12,
    testar: post => post.imagensSemAlt > 0,
    quantidade: post => post.imagensSemAlt,
    mensagem: post =>
      `${post.imagensSemAlt} imagem(ns) sem ALT`,
    recomendacao:
      "Adicionar texto alternativo descritivo"
  }
];

Agora temos algo parecido com uma tabela de parâmetros.

Em vez de alterar dezenas de funções, adicionamos ou modificamos uma entrada no catálogo.


O Executor de Regras

O executor recebe um post e percorre o catálogo.

function executarRegras(post, regras) {
  const ocorrencias = [];

  for (const regra of regras) {
    const resultado = regra.testar(post);

    if (!resultado) {
      continue;
    }

    const quantidade =
      typeof regra.quantidade === "function"
        ? regra.quantidade(post)
        : 1;

    const penalidadeBase =
      typeof regra.penalidade === "function"
        ? regra.penalidade(post, quantidade)
        : regra.penalidade || 0;

    const penalidade = regra.penalidadeMaxima
      ? Math.min(penalidadeBase * quantidade, regra.penalidadeMaxima)
      : penalidadeBase;

    ocorrencias.push({
      codigo: regra.codigo,
      nome: regra.nome,
      categoria: regra.categoria,
      gravidade: regra.gravidade,
      quantidade,
      penalidade,
      mensagem:
        typeof regra.mensagem === "function"
          ? regra.mensagem(post, quantidade)
          : regra.mensagem,
      recomendacao: regra.recomendacao
    });
  }

  return ocorrencias;
}

A lógica está centralizada.

Cada regra decide quando deve disparar.

O executor apenas padroniza o resultado.


Mensagens no Estilo Mainframe

Uma das identidades mais interessantes do Bellacosa Blog Doctor pode ser o uso de mensagens inspiradas no universo IBM.

Exemplo:

BBDSEO001W TITLE LENGTH BELOW RECOMMENDED VALUE
BBDACC002E IMAGE WITHOUT ALT ATTRIBUTE DETECTED
BBDHTML004E TABLE FOUND INSIDE PARAGRAPH
BBDIA001I AI INTERFACE METADATA DETECTED
BBDDOC003W MICROSOFT WORD RESIDUE FOUND

A estrutura pode seguir este padrão:

BBD + MÓDULO + NÚMERO + SEVERIDADE

Exemplo:

BBDSEO001W

Significado:

BBD........Bellacosa Blog Doctor
SEO........Módulo SEO
001........Número da mensagem
W..........Warning

Podemos adotar:

I – Information
W – Warning
E – Error
S – Severe

Assim, cada ocorrência possui uma identidade única.


Definindo os Módulos

O catálogo pode ser separado por áreas.

HTML

Prefixo:

BBDHTML

Exemplos:

BBDHTML001W – Heading vazio
BBDHTML002E – Tabela dentro de parágrafo
BBDHTML003W – Mais de um H1
BBDHTML004W – Excesso de estilos inline

SEO

Prefixo:

BBDSEO

Exemplos:

BBDSEO001W – Título curto
BBDSEO002W – Título longo
BBDSEO003W – Conteúdo curto
BBDSEO004W – Sem links internos

Acessibilidade

Prefixo:

BBDACC

Exemplos:

BBDACC001E – Imagem sem ALT
BBDACC002W – Link sem texto descritivo
BBDACC003W – Heading fora de sequência

Resíduos

Prefixos:

BBDIA
BBDDOC

Exemplos:

BBDIA001I – Resíduo de interface de IA
BBDDOC001W – Resíduo do Microsoft Word
BBDDOC002W – Excesso de spans do Google Docs

Links

Prefixo:

BBDLNK

Exemplos:

BBDLNK001W – Link HTTP
BBDLNK002W – Link vazio
BBDLNK003E – Link JavaScript suspeito

Gravidade Não é a Mesma Coisa que Penalidade

Uma regra pode ter gravidade alta, mas penalidade moderada.

Outra pode ter gravidade baixa e aparecer centenas de vezes.

Precisamos separar os conceitos.

Gravidade

Representa o impacto técnico de uma ocorrência individual.

INFORMATIVA
BAIXA
MÉDIA
ALTA
CRÍTICA

Penalidade

Representa quanto aquela ocorrência reduz o score.

Exemplo:

{
  gravidade: "alta",
  penalidade: 4,
  penalidadeMaxima: 12
}

Se houver uma imagem sem ALT:

Penalidade: 4

Se houver três:

Penalidade: 12

Se houver dez:

Penalidade: continua limitada a 12

Sem um limite, um único tipo de erro poderia zerar o score inteiro.


Penalidade Fixa e Penalidade Progressiva

Existem dois modelos principais.

Penalidade fixa

A regra desconta sempre a mesma quantidade.

penalidade: 8

Exemplo:

Sem links internos: -8

Penalidade progressiva

A penalidade depende da quantidade.

penalidade: 3,
penalidadeMaxima: 12

Exemplo:

1 imagem sem ALT........-3
2 imagens sem ALT.......-6
3 imagens sem ALT.......-9
4 ou mais..............-12

Esse modelo é ideal para ocorrências repetidas.


Regras Dependentes do Contexto

Uma ferramenta inteligente não deve aplicar regras de forma cega.

Imagine a regra:

Post sem link interno

Ela faz sentido para um artigo de 2.000 palavras.

Talvez não faça sentido para um comunicado de 80 palavras ou uma página composta apenas por vídeo.

Logo, a regra precisa de contexto.

{
  codigo: "BBDSEO004W",
  categoria: "SEO",
  gravidade: "media",
  penalidade: 8,

  aplicavel(post) {
    return post.palavras >= 500;
  },

  testar(post) {
    return post.linksInternos === 0;
  }
}

O executor deve verificar primeiro se a regra é aplicável.

if (
  typeof regra.aplicavel === "function" &&
  !regra.aplicavel(post)
) {
  continue;
}

Isso evita falsos positivos.


Falsos Positivos: O Inimigo Invisível

Todo scanner automático corre o risco de acusar problemas onde não existem.

Exemplo:

Conteúdo curto

Um post pode possuir apenas 120 palavras porque contém:

  • um vídeo;

  • um infográfico;

  • uma galeria;

  • um aviso rápido;

  • uma imagem histórica.

Outro exemplo:

Sem heading

Um post de 250 palavras pode não precisar de subtítulos.

O motor deve ser conservador.

Melhor classificar uma situação como “revisar” do que afirmar “erro crítico” sem contexto.

A ferramenta deve usar palavras como:

possível
provável
recomendado
sugestão
revisão manual

O Blog Doctor não é juiz.

É perito.


Regras com Evidências

Um diagnóstico fica muito mais útil quando mostra a evidência.

{
  codigo: "BBDHTML002E",
  mensagem: "Tabela localizada dentro de parágrafo",
  evidencia: "<p><table class=\"...\">",
  posicao: 3842
}

Para capturar trechos:

function extrairEvidencia(html, regex, margem = 80) {
  const match = regex.exec(html);

  if (!match) {
    return "";
  }

  const inicio = Math.max(0, match.index - margem);
  const fim = Math.min(
    html.length,
    match.index + match[0].length + margem
  );

  return html.slice(inicio, fim);
}

Isso funciona como o trecho relevante de um dump.

Não precisamos mostrar o HTML inteiro.

Apenas a região onde o problema foi encontrado.


Regras com Seletores DOM

Nem toda regra precisa usar expressão regular.

Quando o HTML é convertido por DOMParser, podemos usar seletores.

const documento = new DOMParser()
  .parseFromString(post.html, "text/html");

Exemplo de imagens sem ALT:

const imagensSemAlt = [
  ...documento.querySelectorAll("img")
].filter(imagem => {
  return !String(
    imagem.getAttribute("alt") || ""
  ).trim();
});

Headings vazios:

const headingsVazios = [
  ...documento.querySelectorAll("h1,h2,h3,h4,h5,h6")
].filter(heading => !heading.textContent.trim());

Mais de um H1:

const multiplosH1 =
  documento.querySelectorAll("h1").length > 1;

O DOM é ideal para estrutura.

Regex é útil para resíduos ou padrões textuais.

Um bom scanner usa ambos.


Um Exemplo Completo de Regra

const regraImagemSemAlt = {
  codigo: "BBDACC001E",
  nome: "Imagem sem texto alternativo",
  categoria: "Acessibilidade",
  gravidade: "alta",
  penalidade: 3,
  penalidadeMaxima: 12,

  aplicavel(post) {
    return post.imagens > 0;
  },

  testar(post) {
    return post.imagensSemAlt > 0;
  },

  quantidade(post) {
    return post.imagensSemAlt;
  },

  mensagem(post, quantidade) {
    return quantidade === 1
      ? "Foi encontrada uma imagem sem atributo ALT"
      : `Foram encontradas ${quantidade} imagens sem atributo ALT`;
  },

  recomendacao:
    "Adicionar texto alternativo objetivo e descritivo às imagens relevantes"
};

Esse formato pode ser copiado para dezenas de novas regras.


Calculando o Score Final

Depois de executar as regras:

const ocorrencias = executarRegras(post, regras);

Somamos as penalidades.

function calcularScore(ocorrencias) {
  const total = ocorrencias.reduce(
    (soma, ocorrencia) =>
      soma + ocorrencia.penalidade,
    0
  );

  return Math.max(0, 100 - total);
}

Resultado:

Score inicial................100
Título curto.................-7
Imagem sem ALT...............-6
Sem links internos...........-8
Resíduo do Word...............-6

Score final...................73

Scores por Categoria

Um único score geral é útil, mas não conta toda a história.

Podemos calcular scores separados.

HTML.........................84
SEO..........................92
Acessibilidade...............70
Conteúdo.....................96
Links........................88

Algoritmo:

function calcularScoresPorCategoria(ocorrencias) {
  const categorias = {};

  for (const ocorrencia of ocorrencias) {
    if (!categorias[ocorrencia.categoria]) {
      categorias[ocorrencia.categoria] = 100;
    }

    categorias[ocorrencia.categoria] -=
      ocorrencia.penalidade;
  }

  for (const categoria in categorias) {
    categorias[categoria] = Math.max(
      0,
      categorias[categoria]
    );
  }

  return categorias;
}

Isso permite entender onde está o problema.

Um post pode ter SEO excelente, mas acessibilidade fraca.

Outro pode possuir HTML limpo, porém quase nenhum link interno.


Classificação por Faixa

O score pode ser convertido em status.

function classificarScore(score) {
  if (score >= 90) return "saudável";
  if (score >= 75) return "atenção";
  if (score >= 60) return "revisão recomendada";
  if (score >= 40) return "degradado";
  return "crítico";
}

Exemplo:

96........Saudável
83........Atenção
67........Revisão recomendada
48........Degradado
29........Crítico

O Equivalente ao Return Code

No z/OS, o código de retorno resume o resultado de uma etapa.

O Blog Doctor pode adotar uma escala própria.

BDC0000 – Nenhum problema relevante
BDC0004 – Alertas informativos ou leves
BDC0008 – Revisão recomendada
BDC0012 – Erros importantes
BDC0016 – Situação crítica

Algoritmo:

function calcularReturnCode(ocorrencias, score) {
  const possuiCritica = ocorrencias.some(
    item => item.gravidade === "critica"
  );

  const possuiAlta = ocorrencias.some(
    item => item.gravidade === "alta"
  );

  if (possuiCritica || score < 40) {
    return "BDC0016";
  }

  if (possuiAlta || score < 60) {
    return "BDC0012";
  }

  if (score < 75) {
    return "BDC0008";
  }

  if (ocorrencias.length > 0) {
    return "BDC0004";
  }

  return "BDC0000";
}

Isso deixa o relatório muito mais familiar para profissionais de mainframe.


Prioridade de Correção

Gravidade individual não basta.

Precisamos considerar também o alcance.

Uma regra pode ocorrer uma vez ou em mil posts.

Podemos calcular prioridade global.

function calcularPrioridadeGlobal(
  gravidade,
  quantidadePosts,
  totalPosts
) {
  const percentual =
    totalPosts > 0
      ? quantidadePosts / totalPosts
      : 0;

  const pesoGravidade = {
    informativa: 0,
    baixa: 1,
    media: 2,
    alta: 3,
    critica: 4
  };

  const pesoVolume =
    percentual >= 0.50 ? 4 :
    percentual >= 0.20 ? 3 :
    percentual >= 0.05 ? 2 :
    quantidadePosts > 0 ? 1 : 0;

  const total =
    pesoGravidade[gravidade] + pesoVolume;

  if (total >= 7) return "crítica";
  if (total >= 5) return "alta";
  if (total >= 3) return "média";
  return "baixa";
}

Exemplo:

Regra................Imagens sem ALT
Posts afetados........1.240
Total do blog..........4.018
Cobertura..............30,86%
Gravidade..............Alta
Prioridade.............Crítica

Configuração Externa das Regras

Em uma versão avançada, os limites não devem ficar fixos no código.

Podemos criar uma configuração.

const configuracao = {
  titulo: {
    minimo: 20,
    maximo: 70
  },
  conteudo: {
    minimoPalavras: 300,
    exigirHeadingAcimaDe: 500
  },
  marcadores: {
    maximo: 12
  },
  seo: {
    scoreSaudavel: 90
  }
};

A regra consulta a configuração.

testar(post, config) {
  return post.titulo.length <
    config.titulo.minimo;
}

Isso permite adaptar o scanner para outros blogs.


Ativando e Desativando Regras

Nem todas as regras serão úteis para todos os usuários.

Uma regra pode possuir:

ativa: true

O executor ignora regras desativadas.

if (regra.ativa === false) {
  continue;
}

Também podemos filtrar por módulo.

const modulosAtivos = new Set([
  "HTML",
  "SEO",
  "Acessibilidade"
]);

Assim o usuário pode realizar:

  • auditoria completa;

  • apenas SEO;

  • apenas HTML;

  • apenas acessibilidade;

  • apenas resíduos.


Regras por Período Histórico

Um blog com quinze anos não pode ser julgado inteiramente pelos padrões atuais.

Um artigo de 2012 foi criado em outro contexto.

Talvez o tema do Blogger inserisse automaticamente estruturas que hoje parecem estranhas.

Podemos criar regras condicionadas ao ano.

{
  codigo: "BBDHTML010I",

  aplicavel(post) {
    return post.publicadoEm.getFullYear() >= 2020;
  },

  testar(post) {
    return post.excessoEstiloInline;
  }
}

Ou reduzir penalidade em conteúdo antigo.

penalidade(post) {
  const ano = post.publicadoEm.getFullYear();

  return ano < 2015 ? 2 : 6;
}

Isso torna o scanner historicamente consciente.


Versionamento das Regras

O Motor de Regras também precisa de versão.

{
  codigo: "BBDSEO001W",
  versao: "1.2.0",
  criadaEm: "2026-07-26",
  alteradaEm: "2026-08-10"
}

Por quê?

Porque o score de um post pode mudar mesmo sem alteração no conteúdo.

Talvez a regra tenha sido ajustada.

Exemplo:

Auditoria de julho
Regra de título curto: mínimo 15 caracteres

Auditoria de setembro
Regra de título curto: mínimo 20 caracteres

Sem registrar a versão, a comparação histórica fica enganosa.


Testando o Motor de Regras

Nenhum motor deve entrar em produção sem testes.

Podemos criar posts simulados.

const postTeste = {
  titulo: "COBOL",
  palavras: 120,
  imagens: 2,
  imagensSemAlt: 2,
  linksInternos: 0,
  marcadores: [],
  residuosIA: 1
};

Depois executamos.

const ocorrencias =
  executarRegras(postTeste, regras);

console.table(ocorrencias);

Resultado esperado:

BBDSEO001W – Título curto
BBDSEO003W – Conteúdo curto
BBDACC001E – Imagens sem ALT
BBDSEO004W – Sem links internos
BBDSEO006W – Sem marcadores
BBDIA001I  – Resíduo de IA

Testes Unitários

Podemos criar pequenas verificações.

function afirmar(condicao, mensagem) {
  if (!condicao) {
    throw new Error(
      "Teste falhou: " + mensagem
    );
  }
}

const resultado =
  executarRegras(postTeste, regras);

afirmar(
  resultado.some(
    item => item.codigo === "BBDSEO001W"
  ),
  "Deveria detectar título curto"
);

Em uma versão profissional, podemos usar Jest, Vitest ou outra biblioteca.

Mas até testes simples já ajudam.


Evitando Regras Duplicadas

O catálogo deve validar códigos duplicados.

function validarCatalogo(regras) {
  const codigos = new Set();

  for (const regra of regras) {
    if (codigos.has(regra.codigo)) {
      throw new Error(
        `Código duplicado: ${regra.codigo}`
      );
    }

    codigos.add(regra.codigo);
  }
}

Também podemos validar campos obrigatórios.

const obrigatorios = [
  "codigo",
  "categoria",
  "gravidade",
  "testar"
];

É como validar um copybook antes de compilar o programa.


Relatório por Regra

Depois de auditar todos os posts, podemos agrupar ocorrências por código.

function agruparPorRegra(postsAuditados) {
  const resumo = new Map();

  for (const post of postsAuditados) {
    for (const ocorrencia of post.ocorrencias) {
      if (!resumo.has(ocorrencia.codigo)) {
        resumo.set(ocorrencia.codigo, {
          codigo: ocorrencia.codigo,
          mensagem: ocorrencia.mensagem,
          gravidade: ocorrencia.gravidade,
          posts: 0,
          ocorrencias: 0
        });
      }

      const item = resumo.get(ocorrencia.codigo);
      item.posts++;
      item.ocorrencias += ocorrencia.quantidade;
    }
  }

  return [...resumo.values()];
}

Relatório:

BBDACC001E
Posts afetados.............312
Ocorrências................487
Gravidade..................Alta

BBDIA001I
Posts afetados..............14
Ocorrências.................62
Gravidade...........Informativa

Relatório por Post

Cada post recebe:

{
  titulo: "ABEND sem Mistérios",
  score: 82,
  returnCode: "BDC0008",
  status: "revisão recomendada",
  ocorrencias: [...]
}

Tabela:

RC        SCORE  TÍTULO
BDC0000     98   Data Division sem Mistérios
BDC0004     88   O Mercado das Linguagens
BDC0008     72   História do COBOL
BDC0012     54   Post antigo importado
BDC0016     31   Página com HTML corrompido

Recomendação Automática

Cada regra deve sugerir uma ação.

Exemplo:

Problema:
Imagem sem ALT

Recomendação:
Adicionar texto alternativo que descreva a finalidade visual da imagem.

Evitar:
alt="imagem"
alt="foto"
alt="banner"

Preferir:
alt="Fluxo de processamento de cartão via CICS"

Para tabela em parágrafo:

Problema:
Tabela encontrada dentro de parágrafo.

Recomendação:
Fechar o elemento <p> antes da tabela e abrir um novo parágrafo depois dela.

A recomendação deve ser prática.

Não basta dizer “corrigir HTML”.


Ações Automáticas Seguras

Algumas correções podem ser sugeridas ou simuladas.

Exemplo:

function sugerirRemocaoResiduoIA(html) {
  return html
    .replace(/\sdata-message-id="[^"]*"/gi, "")
    .replace(/\sdata-message-author-role="[^"]*"/gi, "")
    .replace(/\sdata-message-model-slug="[^"]*"/gi, "");
}

Mas a ferramenta deve apresentar um diff.

ANTES:
<div data-message-id="abc123" class="markdown">

DEPOIS:
<div class="markdown">

Somente após revisão a mudança poderia ser aplicada.


Regras Informativas

Nem toda regra precisa reduzir score.

Exemplo:

{
  codigo: "BBDINF001I",
  categoria: "Informação",
  gravidade: "informativa",
  penalidade: 0,
  testar(post) {
    return post.palavras >= 5000;
  },
  mensagem(post) {
    return `Artigo longo com ${post.palavras} palavras`;
  },
  recomendacao:
    "Considere criar índice interno ou dividir o conteúdo em partes"
}

Essa regra não acusa erro.

Ela oferece contexto.


Regras Positivas

Também podemos reconhecer boas práticas.

{
  codigo: "BBDPOS001I",
  tipo: "bonus",
  categoria: "Qualidade",
  bonus: 3,
  testar(post) {
    return (
      post.palavras >= 1500 &&
      post.linksInternos >= 3 &&
      post.imagensSemAlt === 0
    );
  },
  mensagem:
    "Artigo extenso, interligado e acessível"
}

Nesse caso, o score poderia receber bônus limitado.

Mas é importante impedir valores acima de 100.

score = Math.min(100, score + bonus);

O Motor como uma Espécie de RACF Editorial

O RACF decide:

  • quem pode acessar;

  • qual recurso está protegido;

  • qual regra se aplica;

  • qual decisão deve ser tomada;

  • qual evento será registrado.

O Motor de Regras do Blog Doctor decide:

  • qual análise se aplica;

  • qual condição foi violada;

  • qual gravidade existe;

  • qual mensagem será registrada;

  • qual recomendação será emitida.

Não é segurança no sentido clássico.

Mas a filosofia é semelhante.

Centralização de regras.

Decisões previsíveis.

Auditoria.

Rastreabilidade.


Log da Auditoria

Cada execução pode gerar um log.

22:31:04 BBD0001I AUDIT STARTED
22:31:04 BBD0002I RULESET VERSION 1.0.0
22:31:05 BBD0003I FEED BATCH 1 LOADED
22:31:18 BBD0004I 100 POSTS PROCESSED
22:36:47 BBD0005I 4018 POSTS PROCESSED
22:36:48 BBD0006I REPORT GENERATED
22:36:48 BBD0007I AUDIT COMPLETED RC=0008

Esse log ajuda a investigar falhas do próprio scanner.


Tratamento de Erros nas Regras

Uma regra mal escrita não deve interromper toda a auditoria.

try {
  const resultado = regra.testar(post);
} catch (erro) {
  ocorrencias.push({
    codigo: "BBDENG999E",
    categoria: "Motor",
    gravidade: "alta",
    penalidade: 0,
    mensagem:
      `Falha ao executar a regra ${regra.codigo}`,
    detalhe: erro.message
  });
}

Assim, o sistema continua processando os demais posts.

É o equivalente a isolar uma etapa com erro sem derrubar o job inteiro.


Performance do Motor

Suponha:

4.000 posts
80 regras

Isso significa:

320.000 avaliações

Parece muito, mas boa parte dessas verificações é simples.

Mesmo assim, podemos otimizar.

Primeiro, o HTML deve ser parseado apenas uma vez por post.

Errado:

cada regra cria um novo DOMParser

Correto:

const documento = parsearHTML(post.html);

const contexto = {
  post,
  documento,
  imagens: [...documento.querySelectorAll("img")],
  links: [...documento.querySelectorAll("a[href]")],
  headings: [...documento.querySelectorAll("h1,h2,h3,h4,h5,h6")]
};

Todas as regras usam o mesmo contexto.


O Contexto de Auditoria

Podemos criar um objeto preparado.

function criarContexto(post) {
  const documento = new DOMParser()
    .parseFromString(post.html, "text/html");

  const imagens = [
    ...documento.querySelectorAll("img")
  ];

  const links = [
    ...documento.querySelectorAll("a[href]")
  ];

  const headings = [
    ...documento.querySelectorAll("h1,h2,h3,h4,h5,h6")
  ];

  return {
    post,
    documento,
    imagens,
    links,
    headings,
    imagensSemAlt: imagens.filter(
      img => !String(
        img.getAttribute("alt") || ""
      ).trim()
    )
  };
}

A regra passa a receber o contexto.

testar(contexto) {
  return contexto.imagensSemAlt.length > 0;
}

Isso reduz processamento repetido.


Processamento em Lotes

Mesmo o Motor de Regras deve trabalhar em lotes.

async function auditarEmLotes(
  posts,
  tamanhoLote = 25
) {
  const resultados = [];

  for (
    let inicio = 0;
    inicio < posts.length;
    inicio += tamanhoLote
  ) {
    const lote = posts.slice(
      inicio,
      inicio + tamanhoLote
    );

    resultados.push(
      ...lote.map(auditarPost)
    );

    atualizarProgresso(
      resultados.length,
      posts.length
    );

    await new Promise(
      resolve => setTimeout(resolve, 0)
    );
  }

  return resultados;
}

A pausa permite que o navegador atualize a tela.


Evolução Futura: Regras Declarativas

Em uma versão mais avançada, algumas regras poderiam ser definidas em JSON.

{
  "codigo": "BBDSEO001W",
  "campo": "titulo.length",
  "operador": "<",
  "valor": 20,
  "gravidade": "media",
  "penalidade": 7
}

O motor interpreta a configuração.

Isso permitiria criar regras sem alterar código.

Mas esse modelo possui limites.

Regras complexas ainda precisariam de JavaScript.

O melhor caminho pode ser híbrido:

  • regras simples declarativas;

  • regras complexas programadas.


O Painel de Regras

O dashboard pode mostrar o catálogo ativo.

CÓDIGO       MÓDULO          STATUS
BBDSEO001W   SEO             ATIVA
BBDSEO002W   SEO             ATIVA
BBDACC001E   ACESSIBILIDADE  ATIVA
BBDIA001I    RESÍDUOS        ATIVA
BBDLNK004E   LINKS REMOTOS   DESATIVADA

Também pode permitir:

  • ativar;

  • desativar;

  • alterar limites;

  • consultar documentação;

  • testar uma regra;

  • exportar configuração.


O Relatório Mestre

Ao final, o Motor de Regras poderá produzir algo como:

BELLACOSA BLOG DOCTOR
RELATÓRIO MESTRE DE AUDITORIA

Blog................Bellacosa Mainframe
Posts...............4.018
Regras ativas..........62
Versão...............1.0.0
Return Code.........BDC0008

SCORE GERAL..............91

HTML.....................93
SEO......................94
ACESSIBILIDADE...........84
LINKS.....................89
CONTEÚDO..................96

TOP 5 OCORRÊNCIAS

1. Imagens sem ALT........312 posts
2. Links HTTP..............87 posts
3. Sem link interno........76 posts
4. Resíduo Word............29 posts
5. Resíduo IA..............14 posts

Isso transforma milhares de ocorrências em uma visão operacional.


O Principal Princípio

O Motor de Regras não deve ser criado para punir conteúdo antigo.

Ele deve ser criado para tornar a manutenção possível.

Seu papel é responder:

  • onde estão os riscos?

  • quais são recorrentes?

  • quais são isolados?

  • quais podem ser ignorados?

  • quais merecem prioridade?

  • quais exigem revisão humana?

  • quais podem ser corrigidos com segurança?

Essa é a diferença entre um scanner útil e uma máquina de produzir alarmes.


Conclusão

A Parte V construiu a arquitetura do Bellacosa Blog Doctor.

A Parte VI construiu seu cérebro.

O Motor de Regras transforma dados técnicos em decisões organizadas.

Ele recebe um post.

Cria um contexto de análise.

Executa regras padronizadas.

Registra evidências.

Calcula penalidades.

Agrupa problemas.

Define gravidade.

Gera recomendações.

Calcula score.

Produz um Return Code.

E entrega um laudo que pode ser compreendido por um programador, editor, administrador ou responsável pelo acervo.

O que antes era apenas uma coleção de if espalhados pelo JavaScript se transforma em uma estrutura comparável aos melhores sistemas de diagnóstico corporativo.

Cada regra possui identidade.

Cada alerta possui código.

Cada penalidade possui limite.

Cada diagnóstico possui evidência.

Cada recomendação possui uma ação.

Esse é o momento em que o Bellacosa Blog Doctor deixa de ser apenas um leitor de Feed Atom.

Ele passa a pensar.

Não como uma inteligência artificial misteriosa.

Mas como um bom programa mainframe.

Com regras claras.

Entradas conhecidas.

Saídas previsíveis.

Mensagens padronizadas.

Logs auditáveis.

E uma responsabilidade fundamental:

Nunca declarar um ABEND editorial sem antes mostrar a evidência, o código, a gravidade e o caminho seguro para a recuperação.

🔎 CSI BLOGSPOT · ARQUIVO DE EVIDÊNCIAS

Blog Analytics: A Investigação Completa

Seis capítulos sobre auditoria de HTML, resíduos invisíveis, limpeza segura, checklist técnico e construção do Bellacosa Blog Doctor.

6 relatórios2018HTML · SEO · Blogger
CASE FILE 01

Blog Analytics — Parte I

Como descobrir evidências invisíveis no HTML de um blog antigo.

HTMLAuditoriaBlogspot
CASE FILE 02

Blog Analytics — Parte II

Os resíduos invisíveis deixados por editores, Word, IA e cópias antigas.

ResíduosWord HTMLIA
CASE FILE 03

Blog Analytics — Parte III

Como limpar milhares de posts sem destruir o acervo editorial.

LimpezaBackupQualidade
CASE FILE 04

Blog Analytics — Parte IV

Checklist de auditoria para blogs antigos e acervos com anos de história.

ChecklistSEO TécnicoAuditoria
CASE FILE 05

Blog Analytics — Parte V

Construindo o Bellacosa Blog Doctor e seu scanner automático.

Blog DoctorScannerJavaScript
CASE FILE 06

Blog Analytics — Parte VI

Criando o motor de regras, scores, prioridades e códigos de retorno.

Motor de RegrasScoreMainframe

Índice textual da série Blog Analytics

Esta série investiga a saúde técnica de blogs antigos no Blogger, cobrindo auditoria de HTML, resíduos de editores, SEO técnico, acessibilidade, limpeza segura, diagnóstico automatizado e motores de regras.

  1. Blog Analytics Parte I — Como descobrir evidências invisíveis
  2. Blog Analytics Parte II — Os resíduos invisíveis do HTML
  3. Blog Analytics Parte III — Como limpar com segurança
  4. Blog Analytics Parte IV — Checklist de auditoria
  5. Blog Analytics Parte V — Construindo o Bellacosa Blog Doctor
  6. Blog Analytics Parte VI — Criando o motor de regras

Sem comentários:

Enviar um comentário

Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988