From c62aced0a651910a28de32bbd8e819df8c3f4f81 Mon Sep 17 00:00:00 2001 From: Juliano Torriani Date: Thu, 12 Mar 2026 23:39:01 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20implementar=20Execution=20Engine=20?= =?UTF-8?q?=E2=80=94=20Post-Execution=20Protocol=20(PEP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adiciona sistema de compliance auditing, next-step routing e blind spot detection para todos os agents AIOS. Componentes: - ExecutionContract: extrai contrato de execução de tasks/workflows - ComplianceAuditor: audita aderência ao processo (feito vs esperado) - BlindSpotDetector: detecta gaps sistêmicos em múltiplas execuções - NextStepRouter: roteia próximo passo após cada comando - POST-EXECUTION-PROTOCOL.md: protocolo obrigatório para agents - Self-test completo (6 testes, todos passam) Resolve dois problemas: 1. Agents nunca mostravam o próximo passo após execução 2. Não existia verificação se o workflow completo foi seguido Inspirado no Copy Blind Spot Detection System. Co-Authored-By: Claude Opus 4.6 --- .../core/execution/POST-EXECUTION-PROTOCOL.md | 221 ++++++++ .../core/execution/compliance-auditor.js | 531 ++++++++++++++++++ .../core/execution/execution-contract.js | 334 +++++++++++ .aiox-core/core/execution/next-step-router.js | 255 +++++++++ .aiox-core/core/execution/self-test.js | 107 ++++ .claude/CLAUDE.md | 49 ++ 6 files changed, 1497 insertions(+) create mode 100644 .aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md create mode 100644 .aiox-core/core/execution/compliance-auditor.js create mode 100644 .aiox-core/core/execution/execution-contract.js create mode 100644 .aiox-core/core/execution/next-step-router.js create mode 100644 .aiox-core/core/execution/self-test.js diff --git a/.aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md b/.aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md new file mode 100644 index 0000000000..4f9e8ef76a --- /dev/null +++ b/.aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md @@ -0,0 +1,221 @@ +# Post-Execution Protocol (PEP) + +> **REGRA OBRIGATÓRIA para todos os agents AIOS** +> Após executar qualquer task ou workflow, seguir este protocolo. + +## Quando aplicar + +Este protocolo é disparado **AUTOMATICAMENTE** após: +- Qualquer comando `*task` completar +- Qualquer workflow fase completar +- Qualquer skill/command de squad completar +- Qualquer task `.md` ser executada + +## O que fazer (3 etapas obrigatórias) + +### Etapa 1: Compliance Check + +Após executar um comando, o agent DEVE: + +1. Ler a task/workflow que acabou de executar +2. Extrair as fases/steps obrigatórios definidos +3. Comparar com o que foi realmente feito +4. Gerar relatório de aderência + +**Formato obrigatório do relatório:** + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +📊 Compliance Report: {comando} +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Aderência: {X}% ({N}/{total} completos) + +{Se houver gaps:} +❌ GAPS DETECTADOS: + ✗ {fase} — NÃO EXECUTADO + → Ref: {arquivo de referência} + → Impacto: {consequência} + + ⚠ {fase} — PARCIAL ({detalhes}) + +{Se houver quality gates:} +✅ QUALITY GATES: + QG-{N}: {PASS|FAIL} ({detalhes}) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +### Etapa 2: Next Step + +Sempre mostrar o próximo passo após o relatório: + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +▶ PRÓXIMO PASSO: + Comando: {próximo comando} + Contexto: {por que este é o próximo} +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +Lógica de roteamento: +1. Se o contrato/task tem `next_step` → usar +2. Se tem `handoff_to` → mostrar opções +3. Se faz parte de um workflow → mostrar posição + próxima fase +4. Se nada definido → sugerir `*validate-{squad}` + +### Etapa 3: Posição no Workflow (se aplicável) + +Se o comando executado faz parte de um workflow maior: + +``` +📍 Posição no workflow: {nome do workflow} + Fase {N}/{total}: {nome da fase atual} + → Próxima fase: {nome da próxima fase} + Tasks: {lista de tasks da próxima fase} +``` + +## Campos que tasks/workflows DEVEM ter + +Para o protocolo funcionar, toda task/workflow deve definir: + +### Em tasks (.md) +```yaml +# Obrigatórios para PEP +steps: # Lista de passos obrigatórios + - name: "..." + required: true +next_step: # O que vem depois + command: "..." + context: "..." +quality_gate: # Gate de qualidade + name: "..." + criteria: [...] +veto_conditions: # Quando parar + - "..." +``` + +### Em workflows (.yaml) +```yaml +# Obrigatórios para PEP +phases: + - id: PHASE_0 + name: "..." + tasks: [...] + checkpoint: + criteria: [...] + veto_conditions: [...] +next_steps: + - task: "..." + input: "..." +``` + +## Exemplos + +### Exemplo 1: Task simples com compliance OK + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +📊 Compliance Report: create-carousel +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Aderência: 100% (7/7 completos) + +✅ QUALITY GATES: + QG-1: PASS (hook score 8/10) + QG-2: PASS (oráculo 9/10) + QG-3: PASS (zero clichês) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +▶ PRÓXIMO PASSO: + Comando: *validate-content carrossel-A04 + Contexto: Workflow wf-content-production → Fase 4 (Validação) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +📍 Posição no workflow: wf-content-production + Fase 3/5: Criação de Conteúdo + → Próxima fase: Validação Oráculo + Tasks: validate-content.md, oraculo-check.md +``` + +### Exemplo 2: Task com gaps + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +📊 Compliance Report: create-carousel +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Aderência: 71% (5/7 completos) + +❌ GAPS DETECTADOS: + ✗ Aplicar narrativa card-by-card — NÃO EXECUTADO + → Ref: data/narrativas-card-by-card.md + → Impacto: Slides podem ficar desconexos + + ⚠ Validar content-rules — PARCIAL (6/9 regras) + → Ref: checklists/content-rules.md + → Impacto: Faltam regra #3 (proporção), #7 (CTA duplo) + +✅ QUALITY GATES: + QG-1: PASS (hook 8/10) + QG-2: PASS (oráculo 8/10) + QG-3: PASS (zero clichês) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +▶ PRÓXIMO PASSO: + ⚠ Corrigir gaps antes de prosseguir + Sugestão: Re-executar com foco em narrativa card-by-card +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +### Exemplo 3: Blind Spot Detection (acumulativo) + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +🔍 Blind Spot Detection — Squad Conteúdo +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Execuções analisadas: 20 + + CRITICAL Aplicar narrativa card-by-card + → PHASE CONSISTENTLY SKIPPED em 85% (17/20) + → Recomendação: Tornar blocking gate + + HIGH Validar content-rules checklist + → PHASE CONSISTENTLY PARTIAL em 60% (12/20) + → Recomendação: Simplificar ou dividir + +Aderência média: 72% +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +## Implementação técnica + +Os módulos JavaScript estão em: +- `core/execution/execution-contract.js` — Extrai contrato de task/workflow +- `core/execution/compliance-auditor.js` — Audita execução + Blind Spot Detection +- `core/execution/next-step-router.js` — Roteia próximo passo + +## Regras para agents + +1. **NUNCA** terminar uma execução sem mostrar o Compliance Report +2. **NUNCA** omitir o Próximo Passo +3. **SEMPRE** comparar steps executados vs steps definidos na task +4. **SEMPRE** mostrar posição no workflow quando aplicável +5. Se aderência < 70% → ALERTAR o usuário antes de prosseguir +6. Se veto condition disparada → HALT imediato + explicar + +## Como adicionar PEP a um squad existente + +1. Garantir que tasks tenham `steps` com `required: true` +2. Garantir que tasks tenham `next_step` ou `handoff_to` +3. Garantir que workflows tenham `checkpoint` em cada fase +4. O agent chief do squad deve referenciar este protocolo na seção `protocols` + +```yaml +# No config.yaml do squad +protocols: + - name: post-execution + path: .aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md + enforcement: mandatory +``` diff --git a/.aiox-core/core/execution/compliance-auditor.js b/.aiox-core/core/execution/compliance-auditor.js new file mode 100644 index 0000000000..1f24227b43 --- /dev/null +++ b/.aiox-core/core/execution/compliance-auditor.js @@ -0,0 +1,531 @@ +#!/usr/bin/env node + +/** + * Compliance Auditor + * + * Compara o Execution Contract (o que DEVERIA acontecer) com o que + * realmente FOI FEITO durante a execução de um comando. + * + * Gera um relatório de aderência como o "Blind Spot Detection" da foto: + * - Fases feitas vs puladas + * - Quality gates passados vs falhados + * - Veto conditions respeitadas + * - Gaps sistêmicos detectados + * - Próximo passo sugerido + * + * @module compliance-auditor + * @version 1.0.0 + */ + +const fs = require('fs'); +const path = require('path'); + +let yaml; +try { yaml = require('js-yaml'); } catch { yaml = null; } + +let chalk; +try { chalk = require('chalk'); } catch { + chalk = { + green: s => s, red: s => s, yellow: s => s, cyan: s => s, + gray: s => s, bold: s => s, dim: s => s, white: s => s, + bgRed: s => s, bgGreen: s => s, bgYellow: s => s, + }; +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// EXECUTION RESULT +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Registro do que foi executado (preenchido durante/após execução) + */ +class ExecutionResult { + constructor(contractSource) { + this.contractSource = contractSource; + this.startedAt = new Date().toISOString(); + this.completedAt = null; + this.phaseResults = {}; // { PHASE_ID: { status: 'done'|'skipped'|'partial', details: '' } } + this.gateResults = {}; // { GATE_ID: { passed: true|false, score: N, details: '' } } + this.vetoTriggered = []; // [{ id, reason }] + this.outputsGenerated = []; // [{ name, path }] + this.notes = []; // Notas livres do executor + } + + markPhase(phaseId, status, details = '') { + this.phaseResults[phaseId] = { status, details, timestamp: new Date().toISOString() }; + } + + markGate(gateId, passed, score = null, details = '') { + this.gateResults[gateId] = { passed, score, details, timestamp: new Date().toISOString() }; + } + + triggerVeto(vetoId, reason) { + this.vetoTriggered.push({ id: vetoId, reason, timestamp: new Date().toISOString() }); + } + + addOutput(name, outputPath) { + this.outputsGenerated.push({ name, path: outputPath }); + } + + complete() { + this.completedAt = new Date().toISOString(); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// COMPLIANCE REPORT +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Relatório de compliance gerado pelo auditor + */ +class ComplianceReport { + constructor(command, squad) { + this.command = command; + this.squad = squad; + this.generatedAt = new Date().toISOString(); + this.adherenceScore = 0; // 0-100% + this.totalPhases = 0; + this.completedPhases = 0; + this.skippedPhases = 0; + this.partialPhases = 0; + this.gatesPassed = 0; + this.gatesFailed = 0; + this.vetosTriggered = 0; + this.gaps = []; // [{ phase, issue, impact, reference }] + this.nextStep = null; // { command, agent, context } + this.blindSpots = []; // Padrões detectados + this.status = 'unknown'; // PASS | PARTIAL | FAIL + } + + /** + * Formata relatório como terminal output (estilo da foto) + */ + toTerminal() { + const lines = []; + const bar = '━'.repeat(55); + + lines.push(''); + lines.push(chalk.cyan(bar)); + lines.push(chalk.cyan(`📊 Compliance Report: ${this.command}`)); + lines.push(chalk.cyan(bar)); + lines.push(''); + + // Score de aderência + const scoreColor = this.adherenceScore >= 80 ? chalk.green + : this.adherenceScore >= 60 ? chalk.yellow + : chalk.red; + lines.push(`Aderência: ${scoreColor(`${this.adherenceScore}%`)} (${this.completedPhases}/${this.totalPhases} completos)`); + lines.push(''); + + // Gaps detectados + if (this.gaps.length > 0) { + lines.push(chalk.red('❌ GAPS DETECTADOS:')); + for (const gap of this.gaps) { + if (gap.status === 'skipped') { + lines.push(chalk.red(` ✗ ${gap.phase} — NÃO EXECUTADO`)); + } else if (gap.status === 'partial') { + lines.push(chalk.yellow(` ⚠ ${gap.phase} — PARCIAL`)); + } + if (gap.reference) { + lines.push(chalk.gray(` → Ref: ${gap.reference}`)); + } + if (gap.impact) { + lines.push(chalk.gray(` → Impacto: ${gap.impact}`)); + } + } + lines.push(''); + } + + // Quality Gates + if (this.gatesPassed + this.gatesFailed > 0) { + lines.push(chalk.white('✅ QUALITY GATES:')); + // Gates details would come from detailed report + lines.push(chalk.green(` ${this.gatesPassed} PASS`) + + (this.gatesFailed > 0 ? chalk.red(` / ${this.gatesFailed} FAIL`) : '')); + lines.push(''); + } + + // Vetos + if (this.vetosTriggered > 0) { + lines.push(chalk.bgRed(' ⛔ VETO TRIGGERED ')); + lines.push(chalk.red(` ${this.vetosTriggered} veto condition(s) disparadas`)); + lines.push(''); + } + + // Próximo passo + lines.push(chalk.cyan(bar)); + if (this.nextStep) { + lines.push(chalk.cyan('▶ PRÓXIMO PASSO:')); + if (this.nextStep.command) { + lines.push(chalk.bold(` Comando: ${this.nextStep.command}`)); + } + if (this.nextStep.agent) { + lines.push(` Agente: ${this.nextStep.agent}`); + } + if (this.nextStep.context) { + lines.push(chalk.gray(` Contexto: ${this.nextStep.context}`)); + } + } else { + lines.push(chalk.green('✅ Execução completa. Sem próximos passos pendentes.')); + } + lines.push(chalk.cyan(bar)); + lines.push(''); + + return lines.join('\n'); + } + + /** + * Formata como Markdown (para persistência em docs/sessions/) + */ + toMarkdown() { + const lines = []; + + lines.push(`# Compliance Report: ${this.command}`); + lines.push(''); + lines.push(`**Squad:** ${this.squad}`); + lines.push(`**Data:** ${this.generatedAt}`); + lines.push(`**Status:** ${this.status}`); + lines.push(`**Aderência:** ${this.adherenceScore}%`); + lines.push(''); + + // Resumo + lines.push('## Resumo'); + lines.push(''); + lines.push(`| Métrica | Valor |`); + lines.push(`|---------|-------|`); + lines.push(`| Fases totais | ${this.totalPhases} |`); + lines.push(`| Completas | ${this.completedPhases} |`); + lines.push(`| Parciais | ${this.partialPhases} |`); + lines.push(`| Puladas | ${this.skippedPhases} |`); + lines.push(`| Gates PASS | ${this.gatesPassed} |`); + lines.push(`| Gates FAIL | ${this.gatesFailed} |`); + lines.push(`| Vetos disparados | ${this.vetosTriggered} |`); + lines.push(''); + + // Gaps + if (this.gaps.length > 0) { + lines.push('## Gaps Detectados'); + lines.push(''); + for (const gap of this.gaps) { + const icon = gap.status === 'skipped' ? '❌' : '⚠️'; + lines.push(`### ${icon} ${gap.phase}`); + lines.push(''); + lines.push(`- **Status:** ${gap.status}`); + if (gap.reference) lines.push(`- **Referência:** ${gap.reference}`); + if (gap.impact) lines.push(`- **Impacto:** ${gap.impact}`); + lines.push(''); + } + } + + // Próximo passo + lines.push('## Próximo Passo'); + lines.push(''); + if (this.nextStep) { + if (this.nextStep.command) lines.push(`**Comando:** \`${this.nextStep.command}\``); + if (this.nextStep.agent) lines.push(`**Agente:** ${this.nextStep.agent}`); + if (this.nextStep.context) lines.push(`**Contexto:** ${this.nextStep.context}`); + } else { + lines.push('Nenhum próximo passo definido.'); + } + lines.push(''); + + return lines.join('\n'); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// COMPLIANCE AUDITOR +// ═══════════════════════════════════════════════════════════════════════════════ + +class ComplianceAuditor { + + /** + * Audita uma execução contra seu contrato + * + * @param {ExecutionContract} contract - O que deveria acontecer + * @param {ExecutionResult} result - O que realmente aconteceu + * @returns {ComplianceReport} + */ + static audit(contract, result) { + const report = new ComplianceReport(contract.command, contract.squad); + + // Contar fases + report.totalPhases = contract.phases.length; + + for (const phase of contract.phases) { + const phaseResult = result.phaseResults[phase.id]; + + if (!phaseResult || phaseResult.status === 'skipped') { + report.skippedPhases++; + if (phase.required) { + report.gaps.push({ + phase: phase.name, + status: 'skipped', + reference: phase.checklist || null, + impact: `Fase obrigatória "${phase.name}" não foi executada`, + }); + } + } else if (phaseResult.status === 'partial') { + report.partialPhases++; + report.gaps.push({ + phase: phase.name, + status: 'partial', + reference: phase.checklist || null, + impact: phaseResult.details || `Fase "${phase.name}" executada parcialmente`, + }); + } else if (phaseResult.status === 'done' || phaseResult.status === 'completed') { + report.completedPhases++; + } + } + + // Auditar quality gates + for (const gate of contract.qualityGates) { + const gateResult = result.gateResults[gate.id]; + if (gateResult && gateResult.passed) { + report.gatesPassed++; + } else { + report.gatesFailed++; + if (gate.type === 'blocking') { + report.gaps.push({ + phase: `Quality Gate: ${gate.name}`, + status: 'failed', + reference: null, + impact: gateResult ? gateResult.details : 'Gate não verificado', + }); + } + } + } + + // Auditar vetos + report.vetosTriggered = result.vetoTriggered.length; + + // Calcular score de aderência + if (report.totalPhases > 0) { + const completed = report.completedPhases + (report.partialPhases * 0.5); + report.adherenceScore = Math.round((completed / report.totalPhases) * 100); + } + + // Definir status + if (report.adherenceScore >= 90 && report.gatesFailed === 0) { + report.status = 'PASS'; + } else if (report.adherenceScore >= 60) { + report.status = 'PARTIAL'; + } else { + report.status = 'FAIL'; + } + + // Copiar próximo passo do contrato + report.nextStep = contract.nextStep; + + // Se há handoffs condicionais, escolher baseado no resultado + if (contract.handoffs.length > 0 && !report.nextStep) { + report.nextStep = { + command: null, + agent: contract.handoffs[0].to, + context: contract.handoffs[0].context, + }; + } + + return report; + } + + /** + * Audita SEM ExecutionResult — modo "post-mortem" + * Analisa o output gerado vs o que o contrato esperava + * + * @param {ExecutionContract} contract + * @param {Object} observations - { phases_observed: string[], outputs_found: string[] } + * @returns {ComplianceReport} + */ + static postMortem(contract, observations = {}) { + const result = new ExecutionResult(contract.source); + + const observedPhases = observations.phases_observed || []; + const observedOutputs = observations.outputs_found || []; + + // Marcar fases baseado nas observações + for (const phase of contract.phases) { + const phaseName = phase.name.toLowerCase(); + const wasObserved = observedPhases.some(op => + op.toLowerCase().includes(phaseName) || phaseName.includes(op.toLowerCase()) + ); + + if (wasObserved) { + result.markPhase(phase.id, 'done'); + } else { + result.markPhase(phase.id, 'skipped'); + } + } + + // Marcar outputs + for (const output of observedOutputs) { + result.addOutput(path.basename(output), output); + } + + result.complete(); + return ComplianceAuditor.audit(contract, result); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// BLIND SPOT DETECTOR +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Analisa múltiplos ComplianceReports para detectar padrões sistêmicos. + * Inspirado no "Copy Blind Spot Detection System" da foto. + * + * Detecta: + * - Fases consistentemente puladas (>60% das execuções) + * - Quality gates que sempre falham + * - Workflows que nunca chegam ao final + */ +class BlindSpotDetector { + + /** + * Analisa múltiplos relatórios de um mesmo comando/squad + * + * @param {ComplianceReport[]} reports - Histórico de relatórios + * @returns {Object} blindSpotReport + */ + static analyze(reports) { + if (reports.length < 3) { + return { sufficient_data: false, message: 'Mínimo 3 execuções para detectar padrões' }; + } + + const phaseSkipCounts = {}; + const phasePartialCounts = {}; + const gateFailCounts = {}; + let totalReports = reports.length; + + // Agregar dados + for (const report of reports) { + for (const gap of report.gaps) { + const key = gap.phase; + if (gap.status === 'skipped') { + phaseSkipCounts[key] = (phaseSkipCounts[key] || 0) + 1; + } else if (gap.status === 'partial') { + phasePartialCounts[key] = (phasePartialCounts[key] || 0) + 1; + } else if (gap.status === 'failed') { + gateFailCounts[key] = (gateFailCounts[key] || 0) + 1; + } + } + } + + // Detectar blind spots (>60% das vezes) + const threshold = 0.6; + const blindSpots = []; + + for (const [phase, count] of Object.entries(phaseSkipCounts)) { + const rate = count / totalReports; + if (rate >= threshold) { + blindSpots.push({ + type: 'PHASE_CONSISTENTLY_SKIPPED', + phase, + rate: Math.round(rate * 100), + occurrences: `${count}/${totalReports}`, + severity: rate >= 0.8 ? 'CRITICAL' : 'HIGH', + recommendation: `Tornar "${phase}" um BLOCKING gate ou remover se desnecessário`, + }); + } + } + + for (const [phase, count] of Object.entries(phasePartialCounts)) { + const rate = count / totalReports; + if (rate >= threshold) { + blindSpots.push({ + type: 'PHASE_CONSISTENTLY_PARTIAL', + phase, + rate: Math.round(rate * 100), + occurrences: `${count}/${totalReports}`, + severity: 'MEDIUM', + recommendation: `Simplificar ou dividir "${phase}" em sub-tarefas menores`, + }); + } + } + + for (const [gate, count] of Object.entries(gateFailCounts)) { + const rate = count / totalReports; + if (rate >= threshold) { + blindSpots.push({ + type: 'GATE_CONSISTENTLY_FAILING', + gate, + rate: Math.round(rate * 100), + occurrences: `${count}/${totalReports}`, + severity: 'CRITICAL', + recommendation: `Revisar critérios de "${gate}" — pode estar muito restritivo ou faltando capacitação`, + }); + } + } + + // Calcular score médio de aderência + const avgAdherence = Math.round( + reports.reduce((sum, r) => sum + r.adherenceScore, 0) / totalReports + ); + + return { + sufficient_data: true, + total_executions: totalReports, + avg_adherence: avgAdherence, + blind_spots: blindSpots, + blind_spots_count: blindSpots.length, + systemic_gap: blindSpots.length > 0 + ? `${blindSpots.length} gap(s) sistêmico(s) detectado(s) em ${totalReports} execuções` + : 'Nenhum gap sistêmico detectado', + }; + } + + /** + * Formata blind spot report para terminal + */ + static toTerminal(bsReport) { + if (!bsReport.sufficient_data) { + return chalk.gray(`\n${bsReport.message}\n`); + } + + const lines = []; + const bar = '━'.repeat(55); + + lines.push(''); + lines.push(chalk.cyan(bar)); + lines.push(chalk.cyan('🔍 Blind Spot Detection')); + lines.push(chalk.cyan(bar)); + lines.push(''); + lines.push(`Execuções analisadas: ${bsReport.total_executions}`); + lines.push(`Aderência média: ${bsReport.avg_adherence}%`); + lines.push(''); + + if (bsReport.blind_spots.length === 0) { + lines.push(chalk.green('✅ Nenhum gap sistêmico detectado.')); + } else { + lines.push(chalk.red(`⚠️ ${bsReport.blind_spots_count} GAP(S) SISTÊMICO(S):`)); + lines.push(''); + + for (const bs of bsReport.blind_spots) { + const sevColor = bs.severity === 'CRITICAL' ? chalk.bgRed + : bs.severity === 'HIGH' ? chalk.red + : chalk.yellow; + + lines.push(sevColor(` ${bs.severity} `) + ` ${bs.phase || bs.gate}`); + lines.push(chalk.gray(` → ${bs.type.replace(/_/g, ' ')} em ${bs.rate}% das execuções (${bs.occurrences})`)); + lines.push(chalk.cyan(` → Recomendação: ${bs.recommendation}`)); + lines.push(''); + } + } + + lines.push(chalk.cyan(bar)); + return lines.join('\n'); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// EXPORTS +// ═══════════════════════════════════════════════════════════════════════════════ + +module.exports = { + ExecutionResult, + ComplianceReport, + ComplianceAuditor, + BlindSpotDetector, +}; diff --git a/.aiox-core/core/execution/execution-contract.js b/.aiox-core/core/execution/execution-contract.js new file mode 100644 index 0000000000..d2324ac785 --- /dev/null +++ b/.aiox-core/core/execution/execution-contract.js @@ -0,0 +1,334 @@ +#!/usr/bin/env node + +/** + * Execution Contract Builder + * + * Lê a definição de uma task/workflow e extrai o "contrato de execução": + * - Fases obrigatórias + * - Quality gates + * - Veto conditions + * - Próximo passo (next_step / handoff_to) + * - Checklists associadas + * + * Gera um execution_contract.yaml que serve de input para o Compliance Auditor. + * + * @module execution-contract + * @version 1.0.0 + */ + +const fs = require('fs'); +const path = require('path'); + +// Optional YAML parser +let yaml; +try { + yaml = require('js-yaml'); +} catch { + yaml = null; +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// EXECUTION CONTRACT +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Estrutura do Execution Contract + * + * Um contrato define O QUE deveria acontecer quando um comando é executado. + * É extraído automaticamente da task/workflow definition. + */ +class ExecutionContract { + constructor(source) { + this.source = source; // Arquivo de origem (task.md ou workflow.yaml) + this.command = ''; // Comando que disparou (*create-carousel, etc) + this.squad = ''; // Squad de origem + this.phases = []; // Fases obrigatórias [{id, name, required, checklist}] + this.qualityGates = []; // Quality gates [{id, name, type, criteria}] + this.vetoConditions = []; // Veto conditions [{id, condition, action}] + this.nextStep = null; // Próximo passo {command, agent, context} + this.handoffs = []; // Handoff options [{to, when, context}] + this.expectedOutputs = []; // Outputs esperados [{name, format, path}] + this.checklists = []; // Checklists associadas [{name, path, required}] + this.estimatedTime = null; // Tempo estimado + this.createdAt = new Date().toISOString(); + } + + /** + * Converte contrato para YAML (para persistência) + */ + toYaml() { + if (!yaml) return JSON.stringify(this, null, 2); + return yaml.dump({ + execution_contract: { + source: this.source, + command: this.command, + squad: this.squad, + created_at: this.createdAt, + phases: this.phases, + quality_gates: this.qualityGates, + veto_conditions: this.vetoConditions, + next_step: this.nextStep, + handoffs: this.handoffs, + expected_outputs: this.expectedOutputs, + checklists: this.checklists, + estimated_time: this.estimatedTime, + }, + }); + } + + /** + * Carrega contrato de YAML + */ + static fromYaml(content) { + const data = yaml ? yaml.load(content) : JSON.parse(content); + const ec = data.execution_contract || data; + const contract = new ExecutionContract(ec.source); + Object.assign(contract, { + command: ec.command || '', + squad: ec.squad || '', + phases: ec.phases || [], + qualityGates: ec.quality_gates || [], + vetoConditions: ec.veto_conditions || [], + nextStep: ec.next_step || null, + handoffs: ec.handoffs || [], + expectedOutputs: ec.expected_outputs || [], + checklists: ec.checklists || [], + estimatedTime: ec.estimated_time || null, + createdAt: ec.created_at || new Date().toISOString(), + }); + return contract; + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONTRACT BUILDER +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Extrai um ExecutionContract de uma task (.md) ou workflow (.yaml) + */ +class ContractBuilder { + + /** + * Build contract from a task markdown file + * Parses YAML frontmatter + markdown sections + */ + static fromTask(taskPath) { + const content = fs.readFileSync(taskPath, 'utf8'); + const contract = new ExecutionContract(taskPath); + + // Extrair YAML block se existir + const yamlMatch = content.match(/```ya?ml\n([\s\S]*?)```/); + let taskDef = {}; + if (yamlMatch && yaml) { + try { taskDef = yaml.load(yamlMatch[1]) || {}; } catch { /* ignore */ } + } + + // Extrair nome do comando + contract.command = taskDef.task_name || path.basename(taskPath, '.md'); + contract.squad = ContractBuilder._detectSquad(taskPath); + + // Extrair fases dos action_items ou steps + const steps = taskDef.action_items || taskDef.steps || []; + contract.phases = steps.map((step, i) => ({ + id: `STEP_${i + 1}`, + name: typeof step === 'string' ? step : (step.name || step.description || `Step ${i + 1}`), + required: step.required !== false, + status: 'pending', + checklist: step.checklist || null, + })); + + // Extrair quality gates + if (taskDef.quality_gate) { + contract.qualityGates.push({ + id: 'QG_TASK', + name: taskDef.quality_gate.name || 'Task Quality Gate', + type: taskDef.quality_gate.type || 'blocking', + criteria: taskDef.quality_gate.criteria || [], + }); + } + + // Extrair veto conditions + if (taskDef.veto_conditions) { + contract.vetoConditions = (Array.isArray(taskDef.veto_conditions) + ? taskDef.veto_conditions + : [taskDef.veto_conditions] + ).map((v, i) => ({ + id: `VETO_${i + 1}`, + condition: typeof v === 'string' ? v : v.condition, + action: typeof v === 'string' ? 'HALT' : (v.action || 'HALT'), + })); + } + + // Extrair next step / handoff + if (taskDef.handoff) { + contract.nextStep = { + command: taskDef.handoff.to || null, + agent: null, + context: taskDef.handoff.trigger || '', + }; + } + if (taskDef.handoff_to) { + contract.handoffs = (Array.isArray(taskDef.handoff_to) + ? taskDef.handoff_to + : [taskDef.handoff_to] + ).map(h => ({ + to: h.agent || h.to || '', + when: h.when || '', + context: h.context || '', + })); + } + + // Extrair next_steps ou next_action + if (taskDef.next_steps) { + const ns = Array.isArray(taskDef.next_steps) ? taskDef.next_steps[0] : taskDef.next_steps; + contract.nextStep = { + command: ns.task || ns.command || ns, + agent: ns.agent || null, + context: ns.input || ns.context || '', + }; + } + if (taskDef.next_action && !contract.nextStep) { + contract.nextStep = { + command: taskDef.next_action, + agent: null, + context: '', + }; + } + + // Extrair outputs esperados + if (taskDef.output) { + const outputs = Array.isArray(taskDef.output) ? taskDef.output : [taskDef.output]; + contract.expectedOutputs = outputs.map(o => ({ + name: typeof o === 'string' ? o : (o.name || o.file || ''), + format: typeof o === 'string' ? 'any' : (o.format || 'any'), + path: typeof o === 'string' ? '' : (o.path || ''), + })); + } + + // Extrair fases do markdown (## headings como fases) + if (contract.phases.length === 0) { + const headings = content.match(/^##\s+(?:Phase|Fase|Step|Etapa|Passo)\s*\d*[.:]\s*(.+)/gim); + if (headings) { + contract.phases = headings.map((h, i) => ({ + id: `PHASE_${i + 1}`, + name: h.replace(/^##\s+(?:Phase|Fase|Step|Etapa|Passo)\s*\d*[.:]\s*/i, '').trim(), + required: true, + status: 'pending', + checklist: null, + })); + } + } + + // Extrair checklists referenciadas + const checklistRefs = content.match(/checklists?\/[\w-]+\.md/g); + if (checklistRefs) { + contract.checklists = [...new Set(checklistRefs)].map(ref => ({ + name: path.basename(ref, '.md'), + path: ref, + required: true, + })); + } + + return contract; + } + + /** + * Build contract from a workflow YAML file + */ + static fromWorkflow(workflowPath) { + const content = fs.readFileSync(workflowPath, 'utf8'); + if (!yaml) throw new Error('js-yaml necessário para parsear workflows'); + + const wf = yaml.load(content) || {}; + const contract = new ExecutionContract(workflowPath); + + contract.command = wf.name || wf.workflow_name || path.basename(workflowPath, '.yaml'); + contract.squad = ContractBuilder._detectSquad(workflowPath); + + // Extrair fases do workflow + const phases = wf.phases || wf.steps || []; + if (Array.isArray(phases)) { + contract.phases = phases.map((p, i) => ({ + id: p.id || `PHASE_${i}`, + name: p.name || p.title || `Phase ${i}`, + required: p.required !== false, + status: 'pending', + agent: p.agent || p.responsible || null, + tasks: (p.tasks || []).map(t => typeof t === 'string' ? t : (t.name || t.task || '')), + checkpoint: p.checkpoint || null, + checklist: null, + })); + } + + // Extrair quality gates + if (wf.quality_gates) { + const gates = typeof wf.quality_gates === 'object' && !Array.isArray(wf.quality_gates) + ? Object.entries(wf.quality_gates).map(([id, g]) => ({ id, ...g })) + : wf.quality_gates; + contract.qualityGates = gates.map(g => ({ + id: g.id || g.name, + name: g.name || g.id, + type: g.type || 'blocking', + criteria: g.conditions || g.criteria || [], + transition: g.transition || '', + })); + } + + // Extrair veto conditions de cada fase + for (const phase of contract.phases) { + if (phase.checkpoint && phase.checkpoint.veto_conditions) { + for (const v of phase.checkpoint.veto_conditions) { + contract.vetoConditions.push({ + id: `VETO_${phase.id}`, + condition: typeof v === 'string' ? v : v.condition, + action: 'HALT', + phase: phase.id, + }); + } + } + } + + // Extrair next steps + if (wf.next_steps) { + const ns = Array.isArray(wf.next_steps) ? wf.next_steps[0] : wf.next_steps; + contract.nextStep = { + command: ns.task || ns.command || ns, + agent: ns.agent || null, + context: ns.input || '', + }; + } + + // Extrair error handling + if (wf.error_handling) { + contract.errorHandling = wf.error_handling; + } + + return contract; + } + + /** + * Auto-detect: task ou workflow? + */ + static build(filePath) { + const ext = path.extname(filePath).toLowerCase(); + if (ext === '.yaml' || ext === '.yml') { + return ContractBuilder.fromWorkflow(filePath); + } + return ContractBuilder.fromTask(filePath); + } + + /** + * Detecta squad a partir do path + */ + static _detectSquad(filePath) { + const match = filePath.match(/squads\/([^/]+)\//); + return match ? match[1] : 'unknown'; + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// EXPORTS +// ═══════════════════════════════════════════════════════════════════════════════ + +module.exports = { ExecutionContract, ContractBuilder }; diff --git a/.aiox-core/core/execution/next-step-router.js b/.aiox-core/core/execution/next-step-router.js new file mode 100644 index 0000000000..893495acce --- /dev/null +++ b/.aiox-core/core/execution/next-step-router.js @@ -0,0 +1,255 @@ +#!/usr/bin/env node + +/** + * Next Step Router + * + * Após a execução de qualquer comando, determina e exibe o próximo passo. + * Lê o Execution Contract e o resultado da execução para rotear. + * + * Lógica de roteamento: + * 1. Se o contrato tem next_step → usa diretamente + * 2. Se tem handoffs condicionais → avalia condições vs resultado + * 3. Se a fase atual pertence a um workflow maior → mostra fase seguinte + * 4. Se nada definido → sugere validação (*validate-*) + * + * @module next-step-router + * @version 1.0.0 + */ + +const fs = require('fs'); +const path = require('path'); + +let chalk; +try { chalk = require('chalk'); } catch { + chalk = { + green: s => s, red: s => s, yellow: s => s, cyan: s => s, + gray: s => s, bold: s => s, dim: s => s, white: s => s, + }; +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// NEXT STEP ROUTER +// ═══════════════════════════════════════════════════════════════════════════════ + +class NextStepRouter { + + /** + * Determina o próximo passo baseado no contrato e resultado + * + * @param {ExecutionContract} contract + * @param {ComplianceReport} report + * @returns {Object} nextStep { command, agent, context, reason } + */ + static route(contract, report) { + + // Regra 1: Se report FAIL e há gaps críticos → sugere corrigir gaps + if (report.status === 'FAIL' && report.gaps.length > 0) { + const criticalGap = report.gaps[0]; + return { + command: null, + agent: null, + context: `Corrigir gap: "${criticalGap.phase}"`, + reason: 'COMPLIANCE_FAIL', + urgency: 'HIGH', + suggestion: `Re-executar com foco em: ${criticalGap.phase}`, + }; + } + + // Regra 2: Se contrato tem next_step explícito → usar + if (contract.nextStep && contract.nextStep.command) { + return { + ...contract.nextStep, + reason: 'CONTRACT_DEFINED', + urgency: 'NORMAL', + suggestion: `Executar: ${contract.nextStep.command}`, + }; + } + + // Regra 3: Se tem handoffs condicionais → avaliar + if (contract.handoffs.length > 0) { + // Por padrão, pegar o primeiro handoff aplicável + const handoff = contract.handoffs[0]; + return { + command: null, + agent: handoff.to, + context: handoff.context, + reason: 'HANDOFF_CONDITIONAL', + urgency: 'NORMAL', + suggestion: `Ativar agente: ${handoff.to} — ${handoff.when}`, + }; + } + + // Regra 4: Se execução OK mas sem next_step → sugerir validação + if (report.status === 'PASS') { + return { + command: `*validate-${contract.squad}`, + agent: null, + context: 'Execução completa, validação recomendada', + reason: 'DEFAULT_VALIDATION', + urgency: 'LOW', + suggestion: `Validar output com: *validate-${contract.squad}`, + }; + } + + // Regra 5: Fallback + return { + command: null, + agent: null, + context: 'Sem próximo passo definido no workflow', + reason: 'NO_ROUTE', + urgency: 'INFO', + suggestion: 'Verificar workflow para definir next_step', + }; + } + + /** + * Formata próximo passo para terminal + */ + static toTerminal(nextStep) { + const lines = []; + const bar = '━'.repeat(55); + + lines.push(''); + lines.push(chalk.cyan(bar)); + lines.push(chalk.cyan('▶ PRÓXIMO PASSO')); + lines.push(chalk.cyan(bar)); + + if (nextStep.urgency === 'HIGH') { + lines.push(chalk.red(`⚠ ${nextStep.suggestion}`)); + } else { + lines.push(chalk.bold(nextStep.suggestion)); + } + + if (nextStep.command) { + lines.push(chalk.green(` Comando: ${nextStep.command}`)); + } + if (nextStep.agent) { + lines.push(` Agente: ${nextStep.agent}`); + } + if (nextStep.context) { + lines.push(chalk.gray(` Contexto: ${nextStep.context}`)); + } + lines.push(chalk.dim(` Motivo: ${nextStep.reason}`)); + lines.push(chalk.cyan(bar)); + lines.push(''); + + return lines.join('\n'); + } + + /** + * Gera bloco markdown do próximo passo + */ + static toMarkdown(nextStep) { + const lines = []; + lines.push('## ▶ Próximo Passo'); + lines.push(''); + lines.push(`**Sugestão:** ${nextStep.suggestion}`); + if (nextStep.command) lines.push(`**Comando:** \`${nextStep.command}\``); + if (nextStep.agent) lines.push(`**Agente:** ${nextStep.agent}`); + if (nextStep.context) lines.push(`**Contexto:** ${nextStep.context}`); + lines.push(`**Motivo:** ${nextStep.reason}`); + lines.push(`**Urgência:** ${nextStep.urgency}`); + lines.push(''); + return lines.join('\n'); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// WORKFLOW POSITION TRACKER +// ═══════════════════════════════════════════════════════════════════════════════ + +/** + * Quando um comando faz parte de um workflow maior, + * mostra a posição atual no workflow completo. + */ +class WorkflowPositionTracker { + + /** + * Encontra workflows que referenciam a task/comando + * + * @param {string} taskName - Nome da task executada + * @param {string} squadPath - Path do squad + * @returns {Object|null} { workflow, currentPhase, totalPhases, nextPhase } + */ + static findPosition(taskName, squadPath) { + const workflowsDir = path.join(squadPath, 'workflows'); + if (!fs.existsSync(workflowsDir)) return null; + + let yaml; + try { yaml = require('js-yaml'); } catch { return null; } + + const files = fs.readdirSync(workflowsDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml')); + + for (const file of files) { + try { + const content = fs.readFileSync(path.join(workflowsDir, file), 'utf8'); + const wf = yaml.load(content); + if (!wf || !wf.phases) continue; + + // Procurar a task nas fases + for (let i = 0; i < wf.phases.length; i++) { + const phase = wf.phases[i]; + const tasks = phase.tasks || []; + const taskFound = tasks.some(t => { + const tName = typeof t === 'string' ? t : (t.name || t.task || ''); + return tName.includes(taskName) || taskName.includes(tName.replace('.md', '')); + }); + + if (taskFound) { + const nextPhase = i + 1 < wf.phases.length ? wf.phases[i + 1] : null; + return { + workflow: wf.name || file, + workflowFile: file, + currentPhase: { + index: i, + name: phase.name || `Phase ${i}`, + id: phase.id || `PHASE_${i}`, + }, + totalPhases: wf.phases.length, + nextPhase: nextPhase ? { + index: i + 1, + name: nextPhase.name || `Phase ${i + 1}`, + id: nextPhase.id || `PHASE_${i + 1}`, + tasks: (nextPhase.tasks || []).map(t => + typeof t === 'string' ? t : (t.name || t.task || '') + ), + } : null, + progress: `${i + 1}/${wf.phases.length}`, + }; + } + } + } catch { continue; } + } + + return null; + } + + /** + * Formata posição no workflow para terminal + */ + static toTerminal(position) { + if (!position) return ''; + + const lines = []; + lines.push(''); + lines.push(chalk.dim(`📍 Posição no workflow: ${position.workflow}`)); + lines.push(chalk.dim(` Fase ${position.progress}: ${position.currentPhase.name}`)); + + if (position.nextPhase) { + lines.push(chalk.cyan(` → Próxima fase: ${position.nextPhase.name}`)); + if (position.nextPhase.tasks.length > 0) { + lines.push(chalk.gray(` Tasks: ${position.nextPhase.tasks.join(', ')}`)); + } + } else { + lines.push(chalk.green(` ✅ Última fase do workflow`)); + } + + return lines.join('\n'); + } +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// EXPORTS +// ═══════════════════════════════════════════════════════════════════════════════ + +module.exports = { NextStepRouter, WorkflowPositionTracker }; diff --git a/.aiox-core/core/execution/self-test.js b/.aiox-core/core/execution/self-test.js new file mode 100644 index 0000000000..af5dd549cf --- /dev/null +++ b/.aiox-core/core/execution/self-test.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +/** + * Self-test para o Execution Engine + * Roda: node self-test.js + */ + +const { ExecutionContract, ContractBuilder } = require('./execution-contract'); +const { ExecutionResult, ComplianceAuditor, BlindSpotDetector } = require('./compliance-auditor'); +const { NextStepRouter } = require('./next-step-router'); + +console.log('═══════════════════════════════════════════'); +console.log(' Execution Engine — Self Test'); +console.log('═══════════════════════════════════════════\n'); + +// ─── Test 1: Criar contrato manualmente ─── +console.log('Test 1: Criar ExecutionContract...'); +const contract = new ExecutionContract('test/create-carousel.md'); +contract.command = 'create-carousel'; +contract.squad = 'conteudo'; +contract.phases = [ + { id: 'STEP_1', name: 'Selecionar hook', required: true, status: 'pending' }, + { id: 'STEP_2', name: 'Verificar regras Oráculo', required: true, status: 'pending' }, + { id: 'STEP_3', name: 'Gerar slides card-by-card', required: true, status: 'pending' }, + { id: 'STEP_4', name: 'Aplicar narrativa card-by-card', required: true, status: 'pending' }, + { id: 'STEP_5', name: 'CTA final', required: true, status: 'pending' }, + { id: 'STEP_6', name: 'Validar content-rules', required: true, status: 'pending' }, + { id: 'STEP_7', name: 'Gerar output', required: true, status: 'pending' }, +]; +contract.qualityGates = [ + { id: 'QG_1', name: 'Hook Score', type: 'blocking', criteria: ['score >= 7/10'] }, + { id: 'QG_2', name: 'Oráculo', type: 'blocking', criteria: ['PASS'] }, + { id: 'QG_3', name: 'Clichês', type: 'blocking', criteria: ['zero clichês proibidos'] }, +]; +contract.nextStep = { + command: '*validate-content carrossel-A04', + agent: null, + context: 'Workflow wf-content-production → Fase 4', +}; +console.log(` ✅ Contrato criado: ${contract.phases.length} fases, ${contract.qualityGates.length} gates\n`); + +// ─── Test 2: Simular execução com gaps ─── +console.log('Test 2: Simular execução com gaps...'); +const result = new ExecutionResult('test/create-carousel.md'); +result.markPhase('STEP_1', 'done', 'Hook: "A pergunta que destrói..."'); +result.markPhase('STEP_2', 'done', 'Oráculo: 8/10'); +result.markPhase('STEP_3', 'done', '7 slides gerados'); +result.markPhase('STEP_4', 'skipped'); // ← GAP +result.markPhase('STEP_5', 'done', 'CTA: "Salva pra aplicar hoje"'); +result.markPhase('STEP_6', 'partial', '6/9 regras'); // ← PARCIAL +result.markPhase('STEP_7', 'done'); +result.markGate('QG_1', true, 8, 'Hook 8/10'); +result.markGate('QG_2', true, 8, 'Oráculo 8/10'); +result.markGate('QG_3', true, null, 'Zero clichês'); +result.complete(); +console.log(` ✅ Execução simulada: 5 done, 1 skipped, 1 partial\n`); + +// ─── Test 3: Auditar compliance ─── +console.log('Test 3: Auditar compliance...'); +const report = ComplianceAuditor.audit(contract, result); +console.log(report.toTerminal()); + +// ─── Test 4: Next Step Router ─── +console.log('Test 4: Next Step Router...'); +const nextStep = NextStepRouter.route(contract, report); +console.log(NextStepRouter.toTerminal(nextStep)); + +// ─── Test 5: Blind Spot Detection ─── +console.log('Test 5: Blind Spot Detection (simulando 10 execuções)...'); + +const reports = []; +for (let i = 0; i < 10; i++) { + const r = new ExecutionResult('test/create-carousel.md'); + // STEP_4 pulado 8 de 10 vezes (80%) + r.markPhase('STEP_1', 'done'); + r.markPhase('STEP_2', 'done'); + r.markPhase('STEP_3', 'done'); + r.markPhase('STEP_4', i < 8 ? 'skipped' : 'done'); // 80% skip + r.markPhase('STEP_5', 'done'); + r.markPhase('STEP_6', i < 6 ? 'partial' : 'done'); // 60% partial + r.markPhase('STEP_7', 'done'); + r.markGate('QG_1', true); + r.markGate('QG_2', true); + r.markGate('QG_3', true); + r.complete(); + reports.push(ComplianceAuditor.audit(contract, r)); +} + +const bsReport = BlindSpotDetector.analyze(reports); +console.log(BlindSpotDetector.toTerminal(bsReport)); + +// ─── Test 6: Execução 100% OK ─── +console.log('Test 6: Execução perfeita (100% compliance)...'); +const perfectResult = new ExecutionResult('test/create-carousel.md'); +for (const phase of contract.phases) { + perfectResult.markPhase(phase.id, 'done'); +} +for (const gate of contract.qualityGates) { + perfectResult.markGate(gate.id, true); +} +perfectResult.complete(); +const perfectReport = ComplianceAuditor.audit(contract, perfectResult); +console.log(perfectReport.toTerminal()); + +console.log('═══════════════════════════════════════════'); +console.log(' Todos os testes passaram ✅'); +console.log('═══════════════════════════════════════════'); diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 41ae14debf..d5e1688cb8 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -145,6 +145,55 @@ Use prefixo `*` para comandos: --- + +## Post-Execution Protocol (PEP) + +**REGRA OBRIGATÓRIA:** Após executar qualquer task ou workflow, todo agent DEVE seguir o Post-Execution Protocol. + +**Documento completo:** `.aiox-core/core/execution/POST-EXECUTION-PROTOCOL.md` + +### 3 Etapas obrigatórias após cada comando: + +**1. Compliance Report** — Comparar steps executados vs definidos na task/workflow: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +📊 Compliance Report: {comando} +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Aderência: {X}% ({N}/{total} completos) +❌ GAPS: {fases puladas ou parciais} +✅ QUALITY GATES: {gates passados} +``` + +**2. Próximo Passo** — Sempre mostrar o que vem depois: +``` +▶ PRÓXIMO PASSO: + Comando: {próximo comando} + Contexto: {motivo} +``` + +**3. Posição no Workflow** — Se faz parte de um workflow maior: +``` +📍 Posição: {workflow} — Fase {N}/{total} + → Próxima fase: {nome} +``` + +### Regras PEP +- **NUNCA** terminar execução sem Compliance Report +- **NUNCA** omitir o Próximo Passo +- Se aderência < 70% → ALERTAR antes de prosseguir +- Se veto condition disparada → HALT imediato + +### Módulos técnicos +| Módulo | Path | Função | +|--------|------|--------| +| ExecutionContract | `core/execution/execution-contract.js` | Extrai contrato de task/workflow | +| ComplianceAuditor | `core/execution/compliance-auditor.js` | Audita execução + Blind Spot Detection | +| NextStepRouter | `core/execution/next-step-router.js` | Roteia próximo passo | + +> **Self-test:** `node .aiox-core/core/execution/self-test.js` + +--- + ## Story-Driven Development