| 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.
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.
Blog Analytics — Parte I
Como descobrir evidências invisíveis no HTML de um blog antigo.
Blog Analytics — Parte II
Os resíduos invisíveis deixados por editores, Word, IA e cópias antigas.
Blog Analytics — Parte III
Como limpar milhares de posts sem destruir o acervo editorial.
Blog Analytics — Parte IV
Checklist de auditoria para blogs antigos e acervos com anos de história.
Blog Analytics — Parte V
Construindo o Bellacosa Blog Doctor e seu scanner automático.
Blog Analytics — Parte VI
Criando o motor de regras, scores, prioridades e códigos de retorno.
Í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.
- Blog Analytics Parte I — Como descobrir evidências invisíveis
- Blog Analytics Parte II — Os resíduos invisíveis do HTML
- Blog Analytics Parte III — Como limpar com segurança
- Blog Analytics Parte IV — Checklist de auditoria
- Blog Analytics Parte V — Construindo o Bellacosa Blog Doctor
- Blog Analytics Parte VI — Criando o motor de regras
Sem comentários:
Enviar um comentário