agentsclimarketplace

Qa loop

Skill pedroberaldo87/pedro-plugins/plugins/qa-loop/skills/qa-loop

Use quando o usuário quer revisar código num loop disciplinado que para por retornos decrescentes (qualidade vs vale a pena), não por zero erros. Ancora no plano de implementação — toda rodada checa fidelidade ao plano e classifica cada finding em implementação, plan-drift ou plano-falho; conserta só implementação com regression gate por conserto; materializa accepted-limits; e entrega relatório humano (HTML) + journal agêntico pra avaliar o nº ideal de loops com o tempo. A skill tem duas fases — uma assintótica de review (severidade variável; e2e/Playwright vira actionable, não bloqueia) e uma Fase Gate absoluta (lint, type, unit e integração 100% verdes no repo inteiro, pré-existentes incluídos, ou gate-red). O motor roda como um Workflow determinístico all-Opus (Opus revisa, Opus planeja, Opus conserta). Substitui /qa, /rev6 e /iterate. Trigger em "/qa-loop", "/qa", "/rev6", "audita o plano", "tá 100%", "revisa isso", "review multi-ângulo", "loopa o review", "revisa até valer a pena".From its SKILL.md

Install
npx -y skills add pedroberaldo87/pedro-plugins --skill qa-loop

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

49.6 KB, ~15.1k tokens by cl100k_base, as published. Nobody here has run it

/qa-loop — Loop de review→conserto disciplinado

Revisa código num loop e para quando não vale mais a pena — não quando chega a zero. Ancora no plano de implementação: o código não pode "melhorar" e afastar do que foi combinado.

Substitui três skills: o esqueleto de loop multi-agente do antigo /qa, as lentes paralelas do antigo /rev6, e a disciplina de regression-gate + baseline do antigo /iterate — tudo num fluxo só.

A ideia central (por que existe)

Um loop "conserte até zero erros" tratado como convergente sobre um problema assintótico (scrubber/parser/ranker/regex/prompt — espaço de input infinito) nunca para sozinho: um finder adversarial SEMPRE acha mais um. "Zero findings" é assíntota, não estado. A skill troca o critério por "uma rodada inteira sem finding novo de severidade real fora dos limites já aceitos" — e gasta a maior parte da disciplina em não gerar regressões ao consertar. (Esse é o eixo assintótico. O outro eixo — os checks objetivos lint/type/unit/integração — é absoluto e vive na Fase Gate; ver "As duas fases".)

A arquitetura em uma frase — motor = Workflow, casca = skill

O motor (revisar → planejar → consertar → checar, em N rodadas) roda como um único Workflow determinístico (a tool Workflow). Cada papel é um agente no tier certo — todos Opus 5, o que muda por etapa é o effort: Opus Revisor (independente) acha, Opus Planejador (adversarial, árbitro único) adjudica, Opus Executor conserta um fix por vez. O gate de regressão, o gate de severidade, o churn detector e a parada são lógica do script (código JS) — não "o Opus lembrar de aplicar a regra a cada rodada".

A casca (esta skill, no loop principal Opus) faz só o que o Workflow não consegue: os 2 toques humanos. O Workflow roda em background e não pergunta nada no meio, então:

  1. Antes de disparar o Workflow — classifica o domínio (pergunta 1× se ambíguo) e lê o journal.
  2. Depois do Workflow retornar — gera o relatório HTML + journal e pede a nota 0-10.

Invocar /qa-loop é o opt-in pra orquestração multi-agente — a skill instrui a chamar Workflow. Por que Workflow e não sub-agente solto: sub-agente solto seria o Opus disparando Task ad-hoc (a regra global do usuário condena, e o guard PreToolUse(Agent) acorda a cada fix). Por que não Agent Team: o fluxo é um pipeline fechado, não conversa aberta entre teammates. Workflow dá determinismo + telemetria + resumível.

As duas fases — máquina assintótica + gate absoluto

A skill roda duas fases com lógicas próprias que convivem — não uma máquina só. Cada finding pertence a UMA delas, e cada fase tem seu próprio critério de "pronto". Confundir as duas é o erro que esta seção existe pra matar: tratar um erro de type como "finding de severidade rebaixável", ou tratar um cheiro de código como "tem que zerar".

Fase Assintótica — revisão de código, aderência ao plano (os 3 buckets), findings de severidade variável (P0-P3), accepted-limits, parada por retornos decrescentes. e2e/Playwright vive aqui: cobertura parcial e honesta, vira actionable no relatório — não bloqueia. É todo o corpo desta skill (os passos abaixo descrevem ela).

Fase Gate — lint, type-check, teste unitário, teste de integração e afins. Absolutista: 100% verde no repo inteiro ou não passou. Binário. Sem severidade, sem julgamento, sem accepted-limit, sem "não vale a pena". É portão. Os checks nunca foram da fase assintótica — são a Fase Gate. Detalhe operacional no Passo 8.0 (gate de saída) + na seção "Detecção de rede".

As fases não são sequenciais no tempo. O gate é invariante de saída, protegido continuamente pelo regression-net por-fix do motor (a suíte roda a cada conserto). A skill só declara sucesso da sessão quando AS DUAS fecham: a Fase Assintótica chega a uma rodada limpa (seu evento interno — retornos decrescentes) E a Fase Gate fica 100% verde. "Rodada limpa" é o desfecho do motor; "sucesso da sessão" é das duas juntas. Gate vermelho nunca é sucesso — a casca seta stopReason='gate-red'.

Input

/qa-loop <alvo> [--plan=<path>] [--floor=P1] [--max-rounds=6] [--domain=auto] [--headless]
  • <alvo> — caminho/pasta, diff ref (HEAD~3..HEAD, staged), PR (#123), ou descrição natural do que foi implementado.
  • --plan — o plano de implementação a auditar contra (.claude/plans/*.md, docs/specs/*.md). Se ausente, tenta achar o plano mais recente; se não houver, roda em modo "review sem plano" (sem o bucket plan-drift/plan-flaw, só implementação — e avisa que está sem âncora).
  • Knobs (todos com default, abaixo).

Modelo & effort por etapa (R8) — contrato em references/r8-tiers.md

É TUDO Opus 5 (contrato R8 desde 2026-07-26): os seis knobs rodam model: 'opus' e o que dispara no tier certo pro PESO da decisão é o effortxhigh no sweep e no confirm-pass, medium na fila mecânica. A semântica dos knobs é o contrato R8 compartilhado com o /sovai, vendorado em references/r8-tiers.md (fonte: _shared/r8-tiers.md — não editar a cópia à mão; scripts/sync-shared.sh --check pega drift). Trocar o tier de uma etapa lá vale pros dois motores. A tabela completa (Etapa · Modelo · Effort · Knob + o que cada knob significa + a regra de tier por rodada) está no arquivo. Abaixo, só onde cada knob entra NESTE motor (revisa→planeja→conserta):

KnobOnde no motor
decompose_modelREVIEW + PLAN da rodada 1 (sweep completo + plano pesado).
coordinate_modelREVIEW + PLAN das rodadas 2+ (caça-regressão + delta).
executor_modelEXEC — aplica 1 fix do bucket 1 por vez.
mechanical_modelFila objetiva da Fase Gate (Passo 8.0) — lint/type/unit/integração, sem julgamento.
diagnose_modelEscalada de churn — mesma função regrediu ≥ churn_threshold → diagnóstico de raiz, não mais remendo.
finalize_modelConfirm-pass — antes de declarar rodada limpa, um re-sweep completo dedicado roda aqui; não confia no resultado mais barato (coordinate_model) da rodada que pareceu limpa.

Parâmetros / knobs

Os 6 model-knobs (decompose_model, coordinate_model, executor_model, mechanical_model, diagnose_model, finalize_model) com seus defaults de modelo/effort vivem no contrato R8 (references/r8-tiers.md) — não repito os defaults aqui pra não reintroduzir o drift. Os knobs de comportamento do loop:

KnobDefaultO que faz
severity_floorP1Conserta P0/P1; P2/P3 viram candidato a accepted-limit. Load-bearing: define "finding de severidade real".
max_rounds6TRAVA DE INCÊNDIO, NÃO META. Quem decide a parada é o gate de severidade. Clampado pela camada de rede no motor: Camada 4 → 2, Camada 5 → 1 (rede fraca não ganha mais voltas de aposta cega).
domainautoauto infere; convergent (tem comando pass/fail objetivo) ou asymptotic.
regression_gateonSempre on — é o coração.
triage_threshold2Com ≥2 findings (ou qualquer alargamento de regra), o PLAN vira tabela formal; com 1, decisão inline.
churn_threshold2≥N regressões auto-infligidas na mesma função → escala pro diagnose_model (para de remendar).
headlessoffModo não-interativo (pro /sovai) — nunca pergunta, alertas de plano não viram fix.

Precedência (R3): flag de invocação > .claude/qa-loop.config.md do projeto > default acima.

Fluxo

digraph qa_loop {
    rankdir=TB;
    "Start" [shape=doublecircle];
    "CASCA: 0. domínio + lê journal" [shape=box];
    "CASCA: 1. contrato + baseline + rede" [shape=box];
    "WORKFLOW: motor (Fase Assintótica)" [shape=box, style=filled];
    "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)" [shape=diamond];
    "CASCA: 8. relatório HTML + journal + nota" [shape=doublecircle];

    "Start" -> "CASCA: 0. domínio + lê journal" -> "CASCA: 1. contrato + baseline + rede";
    "CASCA: 1. contrato + baseline + rede" -> "WORKFLOW: motor (Fase Assintótica)";
    "WORKFLOW: motor (Fase Assintótica)" -> "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)";
    "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)" -> "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)" [label="vermelho → conserta (fila objetiva, mechanical_model) → re-roda"];
    "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)" -> "CASCA: 8. relatório HTML + journal + nota" [label="travou → gate-red (BLOQUEANTE)"];
    "CASCA: 8.0 FASE GATE (lint/type/unit/integração — 100% repo)" -> "CASCA: 8. relatório HTML + journal + nota" [label="verde / gate vazio declarado"];
}

O loop interno (dentro do Workflow), por rodada:

digraph motor {
    rankdir=TB;
    "REVIEW (decompose/coordinate_model)" [shape=box];
    "Gate severidade (JS)" [shape=diamond];
    "CONFIRM (finalize_model, xhigh)" [shape=box, style=filled];
    "PLAN (decompose/coordinate_model)" [shape=box];
    "EXEC (executor_model)" [shape=box];
    "Gate regressão (JS)" [shape=diamond];
    "Reverte (refaz no tier da rodada)" [shape=box];
    "DIAGNOSE (diagnose_model, xhigh)" [shape=box, style=filled];
    "PARA — rodada limpa" [shape=doublecircle];
    "Próxima rodada" [shape=box];

    "REVIEW (decompose/coordinate_model)" -> "Gate severidade (JS)";
    "Gate severidade (JS)" -> "CONFIRM (finalize_model, xhigh)" [label="parece limpa (rodada completa, 0 severo)"];
    "CONFIRM (finalize_model, xhigh)" -> "PARA — rodada limpa" [label="confirma limpa"];
    "CONFIRM (finalize_model, xhigh)" -> "PLAN (decompose/coordinate_model)" [label="achou algo que a rodada barata perdeu"];
    "Gate severidade (JS)" -> "PLAN (decompose/coordinate_model)" [label="há P0/P1"];
    "PLAN (decompose/coordinate_model)" -> "EXEC (executor_model)";
    "EXEC (executor_model)" -> "Gate regressão (JS)";
    "Gate regressão (JS)" -> "Reverte (refaz no tier da rodada)" [label="suíte vermelha, churn<teto"];
    "Gate regressão (JS)" -> "DIAGNOSE (diagnose_model, xhigh)" [label="churn>=teto"];
    "Reverte (refaz no tier da rodada)" -> "EXEC (executor_model)";
    "Gate regressão (JS)" -> "Próxima rodada" [label="verde / fim do bucket"];
    "Próxima rodada" -> "REVIEW (decompose/coordinate_model)";
}

Tier por rodada: rodada 1 = decompose_model (Opus xhigh, planejamento inicial); rodadas 2+ = coordinate_model (Opus high, coordenação rotineira). O nó CONFIRM é dedicado — sempre finalize_model (Opus xhigh), nunca o tier da rodada que pareceu limpa. DIAGNOSE também é dedicado — sempre diagnose_model (Opus xhigh), disparado só quando o churn escala (mesma função regredindo repetidamente).


CASCA — Passo 0 · Classificar domínio + ler journal

Toda sessão roda AS DUAS fases (ver "As duas fases"). A pergunta de domínio NÃO roteia a skill pra um lado só — ela faz duas coisas: define o conteúdo da Fase Gate E calibra o teto da Fase Assintótica.

Pergunta binária e barata: "existe UM comando objetivo com pass/fail (testes/build/lint/type/um curl com jq -e)?"

  • Convergente — sim, há checks objetivos. Eles compõem a Fase Gate (Passo 8.0): lint/type/unit/ integração rodam ao fim como portão absoluto (100% verde no repo inteiro, ou stopReason='gate-red'). Quanto mais forte a suíte, mais forte o gate; o alvo objetivo também dá teto alto pra Fase Assintótica.
  • Assintótico (default pra heurística — scrubber/parser/ranker/regex/prompt/classificador/"achar todos os bugs") — não há estado-alvo binário pro review; governa o gate de severidade e a parada por retornos decrescentes da Fase Assintótica. É o caso primário desta skill. O domínio NÃO decide se há Fase Gate: se o projeto tem checks objetivos (até um scrubber tem lint+unit), eles compõem a Fase Gate absoluta igual ao caso convergente — o domínio só muda o teto/severidade da fase assintótica. Só SEM nenhum check objetivo a Fase Gate fica vazia, declarada honesto — nunca finge gate.

Se ambíguo e não headless, pergunta 1× ao usuário. Se headless, assume asymptotic (mais conservador).

Lê o journal (R7) antes de disparar o motor: do .claude/qa-loop.config.md do projeto puxa os accepted_limits ratificados + as invariants vivas; de ~/.claude/qa-loop/journal/learnings.md puxa os aprendizados cross-projeto que afinam os prompts do Revisor/Planejador. Tudo isso vira args do Workflow.

⚠️ Erro que mata o loop: tratar assintótico como convergente → loop infinito. A pergunta tem que ser real, não um carimbo.

CASCA — Passo 1 · Declarar contrato + baseline + rede de regressão

Fixa e anuncia no header: teto de rodadas (safety-cap), severity_floor, o plano-âncora, e a camada de rede de regressão disponível (ver Guard-rails → Detecção de rede). Snapshot do nº de erros de lint/typecheck ANTES da rodada 1 (baseline_errors) — é o que a rede de regressão DURANTE o loop usa pra pegar regressão estrutural (não piorar o baseline mid-loop). ⚠️ Isso é a rede in-loop, NÃO o gate: a Fase Gate de saída (Passo 8.0) exige lint/type/unit/integração absolutamente 0 no fim, pré-existentes incluídos — o baseline tolera no meio, o gate não tolera no fim. Declara honestamente o blast-radius que a rede NÃO cobre. Esses valores entram nos args do Workflow.


WORKFLOW — o motor (loop de rodadas)

A casca dispara a tool Workflow com o script abaixo. Esqueleto de referência — o princípio, não código imutável. Os três schemas (FINDINGS, PLAN, EXEC_RESULT) são o que torna os gates determinísticos: o script lê campos estruturados, não texto solto.

export const meta = {
  name: 'qa-loop-engine',
  description: 'Motor de QA: tier por etapa (R8) — decompose/coordinate/executor/mechanical/diagnose/finalize',
  phases: [{ title: 'Review' }, { title: 'Plan' }, { title: 'Exec' }, { title: 'Confirm' }],
}

// args (vindos da casca): { target, planPath, severityFloor, maxRounds, domain,
//   safetyLayer, churnThreshold, acceptedLimits[], invariants[], learnings }
const sevRank = s => ({P0:3, P1:2, P2:1, P3:0}[s] ?? 0)
const floor = sevRank(args.severityFloor || 'P1')
let acceptedLimits = args.acceptedLimits || []
let invariants = args.invariants || []
const churn = {}                 // { 'arquivo:função': nº de regressões }
// chave do churn = arquivo:função. Só o nome da função colidiria entre arquivos
// (dois `validate()` em módulos diferentes) e escalaria o diagnóstico caro cedo demais.
const churnKey = fix => `${fix.file || '?'}:${fix.fn || '?'}`
const rounds = []
let cleanRound = false, churnEscalated = false, r = 0
let touchedLastRound = [], openFindings = []   // delta pro REVIEW das rodadas 2+

// Teto de rodadas: default DENTRO do motor (sem ele, args.maxRounds undefined faz
// `r < undefined` ser false na 1ª volta — o motor devolveria "limpo" sem ter revisado
// nada), e CLAMP pela camada de rede — "rede fraca baixa o teto" é lógica do script,
// não regra pro modelo lembrar (Camada 4 = teto 2, Camada 5 = teto 1).
const LAYER_CAP = { 4: 2, 5: 1 }
const churnThreshold = args.churnThreshold || 2
const maxRounds = Math.min(args.maxRounds || 6, LAYER_CAP[args.safetyLayer] ?? Infinity)

// Tier por rodada (R8 — tabela única com /sovai): rodada 1 = decompose_model (xhigh,
// planejamento inicial); rodadas 2+ = coordinate_model (high, coordenação rotineira).
const tierFor = round => round === 1
  ? { model: 'opus', effort: 'xhigh' }   // decompose_model
  : { model: 'opus', effort: 'high' }    // coordinate_model

while (!cleanRound && r < maxRounds && !churnEscalated) {
  r++; phase(`Rodada ${r}`)
  const tier = tierFor(r)

  // REVIEW — 1 Opus Revisor INDEPENDENTE, no tier da rodada. Checklist de 6 dimensões
  // (1 agente, não 6). Rodada 1 = sweep completo do material inteiro (decompose_model);
  // 2+ = DELTA (coordinate_model): só os arquivos tocados pelos fixes da rodada anterior
  // + findings abertos — caça-regressão, não releitura do material inteiro.
  const review = await agent(reviewPrompt({ round: r, acceptedLimits, invariants,
      scope: r === 1 ? 'full' : { touchedFiles: touchedLastRound, openFindings } }),
    { model: tier.model, effort: tier.effort, phase: 'Review', schema: FINDINGS, agentType: 'voltagent-qa-sec:code-reviewer' })

  // GATE de severidade (JS) — sobre rodada COMPLETA (todo ângulo retornou).
  const severe = review.findings.filter(f =>
    sevRank(f.severity) >= floor && !isAccepted(f, acceptedLimits))

  if (review.complete && severe.length === 0) {
    // CONFIRM — finalize_model (Opus xhigh, R8 "revisão final e integração"). Re-sweep
    // completo DEDICADO antes de declarar limpa — não confia no resultado mais barato
    // (coordinate_model) da rodada que pareceu limpa.
    const confirm = await agent(reviewPrompt({ round: r, acceptedLimits, invariants, confirming: true }),
      { model: 'opus', effort: 'xhigh', phase: 'Confirm', schema: FINDINGS, agentType: 'voltagent-qa-sec:security-auditor' })   // finalize_model
    const confirmSevere = confirm.findings.filter(f =>
      sevRank(f.severity) >= floor && !isAccepted(f, acceptedLimits))
    if (confirm.complete && confirmSevere.length === 0) {
      rounds.push({ r, review, confirm, corrections: [], regressions: 0, alerts: [] })
      cleanRound = true; break
    }
    // o confirm-pass achou algo que a rodada barata perdeu — processa, não ignora.
    review.findings = review.findings.concat(confirm.findings)
  }

  // PLAN — Opus Planejador ADVERSARIAL = árbitro único (R2), no MESMO tier da rodada.
  // Adjudica "procede?" contra a rubrica, roteia nos 3 buckets, triagem por severidade
  // E risco-de-conflito, sequencia. Rodada 1 (decompose_model) pesada; 2+ (coordinate_model) = só o DELTA.
  const plan = await agent(planPrompt({ review, round: r, invariants, acceptedLimits }),
    { model: tier.model, effort: tier.effort, phase: 'Plan', schema: PLAN, agentType: 'voltagent-qa-sec:error-detective' })

  // EXEC — executor_model (Opus high, R8 all-Opus), SÓ bucket 1, EM SÉRIE (o gate roda a
  // suíte entre fixes; pares de risco exigem ordem).
  const corrections = []; let regressions = 0
  for (const fix of plan.bucket1) {
    const res = await agent(execPrompt({ fix, invariants, safetyLayer: args.safetyLayer }),
      { model: 'opus', effort: 'high', phase: 'Exec', schema: EXEC_RESULT, agentType: 'voltagent-core-dev:backend-developer' })   // executor_model
    // GATE de regressão (JS) — quem decide keep/revert é o SCRIPT, nunca o executor.
    if (res.suiteRegressed) {
      const k = churnKey(fix)
      regressions++; churn[k] = (churn[k] || 0) + 1
      if (churn[k] >= churnThreshold) {
        // DIAGNOSE — diagnose_model (Opus xhigh, R8 "diagnóstico após falhas repetidas").
        // Não é mais "refaz cirúrgico" — é a causa raiz do acoplamento que faz a mesma
        // função regredir de novo a cada tentativa.
        await agent(diagnosePrompt({ fix, churnCount: churn[k], invariants }),
          { model: 'opus', effort: 'xhigh', phase: 'Diagnose', agentType: 'voltagent-qa-sec:architect-reviewer' })   // diagnose_model
        churnEscalated = true; break
      }
      await revertAndMaybeRedo(fix, res, tier)   // reverte; refaz cirúrgico no tier DA RODADA
    } else {
      corrections.push(res)
      if (res.newInvariant) invariants.push(res.newInvariant)   // invariante viva pras próximas rodadas
    }
  }

  acceptedLimits = acceptedLimits.concat(plan.proposedLimits || [])   // propostos (não ratificados — R6)
  touchedLastRound = corrections.flatMap(c => c.files_touched || [])  // delta do REVIEW da próxima rodada
  openFindings = review.findings.filter(f => !corrections.some(c => c.fix_id === f.id))
  rounds.push({ r, review, plan, corrections, regressions, alerts: plan.alerts || [] })
}

return {
  rounds, acceptedLimits, invariants, churn,
  planFlawAlerts: rounds.flatMap(x => x.alerts),
  telemetry: rounds.map(x => ({ round: x.r, corrections: x.corrections.length, regressions: x.regressions,
    findings_by_sev: tallyBySev(x.review.findings) })),
  stopReason: cleanRound ? 'no-severe-finding' : churnEscalated ? 'churn-escalated' : 'max-rounds',
}

Schemas (JSON Schema, resumidos):

  • FINDINGS{ complete: boolean, findings: [{ id, file, line, severity: 'P0'|'P1'|'P2'|'P3', dimension, problem, fix_direction }] }. complete=false se algum ângulo não retornou → NUNCA conta como rodada limpa.
  • PLAN{ bucket1: [{ id, fn, severity, conflict_risk, order, fix_direction }], drift: [...], alerts: [...], proposedLimits: [...], invariants: [...] }.
  • EXEC_RESULT{ fix_id, fn, files_touched: [...], test_name, suiteRegressed: boolean, newInvariant?, note }.

Os agentType: voltagent-* nos spawns são otimização de persona — se o agent type não existir na máquina, spawne sem agentType (o motor não depende deles).

Os passos do motor, em detalhe

REVIEW = 1 Opus Revisor (R1), no tier da rodada (R8). Um único Opus cobre as 6 dimensões como CHECKLIST (arquitetura · backend · frontend · contratos fullstack · correção · UX) — não 6 agentes. Rodada 1 roda em decompose_model (xhigh — sweep completo): recebe o material inteiro + o plano-âncora + os accepted-limits vivos (não re-reportar) + as invariantes vivas (não violar). Rodadas 2+ rodam em coordinate_model (high) e recebem o DELTA, não o material inteiro: os arquivos tocados pelos fixes da rodada anterior + os findings ainda abertos + accepted-limits/invariantes (pequenos) — caça-regressão nas mudanças + ângulos frescos sobre elas. Formato de cada finding: P{0-3} — {arquivo:linha} — {problema} — {direção de fix, SEM código}. Inclui sempre duas âncoras (nas rodadas 2+, as duas restritas ao delta):

  • Plano — "compare contra o PLANO: sinalize onde a implementação DIVERGE do planejado, mesmo que o código pareça bom".
  • Constituição — "leia .claude/docs/quality-goals.md do projeto (as metas de qualidade autorais) e sinalize onde a implementação VIOLA o que está escrito lá". O arquivo é lido na rodada, nunca copiado aqui — a régua é a do projeto que instalou, não a desta skill. Sem esse arquivo, o eixo simplesmente não roda e o REVIEW segue só com o plano — ausência de constituição não é finding.

Regra dura: se algum ângulo do checklist não foi coberto → complete=false, jamais "achou zero".

CONFIRM = Opus dedicado em finalize_model (xhigh, R8 "revisão final e integração"). Quando uma rodada parece limpa (complete && severe.length===0), o motor NÃO declara vitória direto — dispara um re-sweep completo independente em finalize_model, sempre xhigh, mesmo que a rodada que pareceu limpa tenha rodado em coordinate_model. Só confirma limpa se o confirm-pass TAMBÉM achar zero; senão os findings do confirm-pass entram no PLAN da mesma rodada — o resultado mais barato nunca tem a palavra final.

PLAN = Opus Planejador adversarial, árbitro único (R2), no MESMO tier da rodada. Entre "achou" e "consertar". 4 sub-passos: (a) consolida + dedup os findings; (b) rotula cada um por BUCKET (impl / plan-drift / plan-flaw); (c) triagem do bucket 1 por severidade e risco de conflito (3 sinais checáveis ANTES de editar — dois fixes na mesma função / fix que ALARGA regra genérica / fix que viola invariante viva); (d) sequencia (extensão-enumerada primeiro, alargamento por último com negative-tests, agrupa por função). Mandato adversarial: default a REJEITAR um finding (ou um accepted-limit) a não ser que se justifique contra a rubrica. Gerar ≠ julgar — o Revisor acha, o Planejador é quem carimba a severidade (mata a oscilação entre rodadas). Rodada 1 (decompose_model) = plano pesado; 2+ (coordinate_model) processam só o DELTA.

EXEC = Opus Executor em executor_model (R1). SÓ bucket 1, um fix por vez, na ordem do PLAN: (1) test-first (red) — escreve o teste que reproduz o finding e vê falhar; o teste recebe nome com a invariante (test_atravessa_placeholder_pro_par_aws); (2) fix cirúrgico — menor mudança, extensão enumerada > alargamento de regra; (3) roda a SUÍTE INTEIRA; (4) reporta. O executor nunca toca bucket 2/3. Ele ser Opus não relaxa nenhuma dessas travas: o papel é estreito por design, não por limitação de modelo.

GATE de regressão (código, não LLM). O EXEC_RESULT.suiteRegressed é objetivo (a suíte passou ou não). Vermelho → reverte + churn++; o Planejador (no tier da rodada) decide refazer cirúrgico (troca difuso por extensão enumerada / exceção mais específica) — até churn_threshold. A partir do threshold, escala pra diagnose_model (Opus xhigh, R8 "diagnóstico após falhas repetidas"): não é mais "refaz cirúrgico", é achar a causa raiz do acoplamento. Quem decide keep/revert é sempre o script + o Planejador, nunca o executor que fez o fix — é separação de papel, não desconfiança de modelo (agora os dois são Opus).

Bucket 2 (plan-drift) e bucket 3 (plan-flaw) nunca viram edição do executor (R4 e a constraint central abaixo).


CONSTRAINT CENTRAL — QA ancorado no plano (3 buckets)

A skill não é só review de código — audita fidelidade ao plano e à constituição do projeto a cada rodada. Todo finding é roteado em 3 buckets no PLAN:

  1. Implementação — código diverge do plano ou viola a constituição do projeto (.claude/docs/quality-goals.md, quando o projeto tem uma), bug, ou regressão. → fila de conserto (EXEC, com gate). Violar a constituição é divergência igual às outras: entra pela rubrica de severidade normal, sem faixa própria.
  2. Plan-drift (R4) — um "fix" otimizaria o código mas afastaria o comportamento do que foi pedido/planejado. → restaura pro plano automaticamente E o desvio sobe no bucket de alertas como candidato a mudança-de-plano pro usuário julgar. O plano vence a "melhoria", mesmo que o agente ache que faz sentido mudar. Drift é uma classe de regressão — não pausa.
  3. Plano/arquitetura falho — o plano em si é falho (decisão de arquitetura que gera problema crítico). → bucket de ALERTA. NUNCA consertado/implementado no loop. Sobe pro usuário no relatório: "apresento e julgamos". É insumo pro planejamento, não trabalho de QA.

A skill enforça o plano; não o redesenha. Só o bucket 1 vira edição. "QA é QA."

Rubrica de severidade (R2) — 3 faixas, escrita, aplicada pelo árbitro único

  • P0/P1 (acima do floor) — exige gatilho objetivo OU ancorado no plano: quebra comportamento do plano · classe de segurança (secret, injection, authz) · bug com repro determinístico. (Falha objetiva de lint/type/unit/integração NÃO entra nesta escada — é da Fase Gate, absoluta e separada.)
  • P2/P3 (abaixo do floor) — opinião de qualidade sem divergência do plano e sem falha objetiva (naming, gosto, micro-refator). Picuinha nunca sobe sozinha — só vira P1 com bug concreto anexado.
  • O Opus Planejador é o ÁRBITRO ÚNICO que aplica a rubrica. Não são N LLMs cada um carimbando — é um juiz, rubrica escrita, severidade comparável entre rodadas (e na telemetria).

Config em 3 camadas (R3) — .claude/qa-loop.config.md (VERSIONADO)

Fora da pasta ignorada .claude/qa-loop/. Versionado de propósito — viaja com o projeto. Contém:

  • Knobs do projeto (floor, max_rounds, churn_threshold) que sobrescrevem o default.
  • Rubrica do projeto — ajustes de severidade específicos do alvo.
  • Accepted-limits permanentes (ratificados pelo usuário — ver R6) + invariantes vivas.
  • Flags de invocação padrão.

Precedência: flag > config do projeto > default da skill.

Critério de parada (DUAS condições: Fase Assintótica + Fase Gate)

A skill só declara sucesso quando AS DUAS fecham. São critérios independentes.

Fase Assintótica — para se qualquer um (gate de severidade primário, teto = trava):

  • [PRIMÁRIO] uma rodada INTEIRA completou (complete=true) E produziu ZERO findings novos de severidade ≥ floor FORA dos accepted-limits E o confirm-pass dedicado (finalize_model, xhigh) concordou — rodada limpa nunca é declarada só pelo resultado mais barato (coordinate_model) que pareceu limpo.
  • [TRAVA] atingiu max_rounds → reporta "teto atingido sem convergir" (ALARME, não sucesso).
  • [ESCALADA] churn escalou (≥churn_threshold regressões na mesma função) → passa por diagnose_model (xhigh) antes de virar churn-escalated.

Fase Gate — condição ABSOLUTA de sucesso (não é "parada por retorno decrescente"):

  • Sucesso EXIGE a Fase Gate 100% verde (lint/type/unit/integração no repo inteiro, incluindo pré-existentes). Vermelho → vira fila de conserto; se travar, stopReason='gate-red' (fracasso explícito, nunca sucesso). e2e/Playwright NÃO entra aqui — é Fase Assintótica, vira actionable.
  • Projeto sem checks objetivos → Fase Gate vazia, declarada honesto (não é sucesso fingido nem gate-red).

O teto NÃO é meta. Quem decide é o gate de severidade — para na rodada 2 se convergiu rápido, vai até o teto se ainda acha P0. Cravar um número fixo agora seria o erro simétrico ao "até zero". A telemetria (passo 8) é que vai dizer, com o tempo, se vale cravar.

"Vale a pena" (R5) — retornos decrescentes + risco de regressão, NUNCA tokens

O eixo "vale a pena" é o gate de severidade (retornos decrescentes) + o regression gate / churn detector (risco de regressão — o churn é o "agora gera mais regressão que conserto, para"). Tokens = número PASSIVO no journal, nunca eixo de parada nem framing de "custo".


CASCA — Passo 8 · Fase Gate (saída) + Relatório (humano) + Journal (agêntico) — R7

Quando o Workflow retorna, a casca executa a Fase Gate e SÓ ENTÃO produz os artefatos.

Passo 8.0 · FASE GATE — o portão absoluto de saída

Antes de qualquer relatório, a casca roda os checks objetivos do projeto como portão binário:

  • Cache verde (consulta → grava): source "${CLAUDE_PLUGIN_ROOT}/lib/green-cache.sh". Antes de rodar a fila, green_cache_check <repo-root> full: HIT → declara o gate verde via cache e reporta no relatório ("gate 100% via cache — tree <hash>, gravado por <writer> às <ts>") sem re-executar. MISS → roda a fila normal; ao fechar 100% verde, green_cache_mark <repo-root> full qa-loop-gate. Gate vermelho nunca grava. Falha do helper (sem git, fora de repo) → MISS silencioso, roda tudo. Cache HIT não é burla: é a mesma fila, verde, no mesmo tree-hash — qualquer edição invalida.
  • O que roda: lint + type-check + teste unitário + teste de integração. A detecção reusa as camadas já definidas (ver "Detecção de rede"): unit/integração = Camada 1; lint/type = Camada 3. e2e marcado (@pytest.mark.e2e, specs Playwright, script e2e) é EXCLUÍDO daqui → vira actionable na Fase Assintótica (lento demais e dependente de ambiente pra ser portão).
  • Escopo: repo inteiro, absoluto. Exige TODO o lint/type/unit/integração do repo 100% verde — incluindo erros pré-existentes não-relacionados à revisão. "Tá errado = corrige = fim." Sem severidade, sem accepted-limit, sem "retornos decrescentes". (Mesma disciplina do /ship, rodada mais cedo.) Se o projeto oferece um runner com escopo próprio (scripts/run_app_tests.sh, target de Makefile, pnpm --filter, cargo test -p), use-o pra rodar no ambiente certo — nunca pra encolher o que precisa passar; em monorepo de múltiplos ambientes é o que evita falso-vermelho de import.
  • Vermelho → conserta em mechanical_model (fila objetiva, FORA do roteamento de buckets). Erro de lint/type/unit/integração é objetivo e determinístico — não precisa do julgamento de severidade do REVIEW→PLAN, nem do effort caro do executor_model. Vai direto pra uma fila de conserto em mechanical_model (opus/medium, R8 "operações mecânicas e bem delimitadas" — mesmo regression gate por conserto), re-roda o gate, até verde. Não passa pelos 3 buckets (esses são pro review subjetivo da fase assintótica). Se travar de verdade (erro que exige decisão de arquitetura), NÃO declara sucesso — e o conserto que travou escala pra diagnose_model (xhigh) antes de virar gate-red.
  • Quem seta o stopReason FINAL é a CASCA, não o motor. O motor (Workflow) reporta o stopReason da fase assintótica (no-severe-finding / churn-escalated / max-rounds); a casca, pós-gate, computa o stopReason da sessão — promove a gate-red quando o gate trava vermelho, e mantém o do motor quando o gate fecha verde. O relatório lidera com "GATE VERMELHO — bloqueante" sempre que gate-red.
  • Transparência (anti-violação-de-surgical-changes). O conserto repo-wide é real, mas o relatório separa duas pilhas — "consertos da revisão" vs "débito pré-existente que precisei zerar pro gate ficar verde" — cada uma com seu diff, pra o usuário ver e reverter o que quiser.
  • Sem checks no projeto → Fase Gate vazia, declarada honesto ("sem checks objetivos — só a fase assintótica rodou"). Nunca finge portão.

Passada a Fase Gate, a casca produz DOIS artefatos com públicos distintos:

(A) Relatório HUMANO — gerador de actionables, via a skill /visual como parceira

INVOQUE a skill /visual pra renderizar (não reimplemente template nem daemon): o /visual já traz a hierarquia, o "pedido se explica sozinho", o vocabulário banido, o daemon de live-sync e a semântica de copy. Você passa o conteúdo estruturado (do return do Workflow); o /visual resolve template + daemon + path + abre.

A página é EMITIDA por programa, não digitada (desde o visual v1.8.0). Você escreve um spec JSON e roda python3 <visual>/lib/visual_page.py build --spec <f>; o contrato dos blocos sai de visual_page.py schema. Isso não é detalhe de forma — é o que garante, sem depender de você lembrar, as duas regras duras desta seção: nenhum feedback-item nasce selecionado e o contador começa em 0 de N. A estrutura abaixo mapeia assim: faixa de status → bloco callout; gráfico → bloco chart; cada actionable → bloco item (com tri quando for bloqueio) e item_labels: ["✓ Vira ação", "✏️ Ação c/ ajuste", "✗ Descartar"]; fechamento → automático. O build recusa relatório que peça veredito sem nenhuma prova na página.

Estrutura do relatório (no topo → fundo):

  • Faixa de STATUS DA FASE GATE (topo de tudo, read-only): verde "gate 100% — N checks" ou vermelho "GATE VERMELHO — bloqueante" (stopReason='gate-red'). Se houve conserto repo-wide, separa duas pilhas com diff — "consertos da revisão" vs "débito pré-existente zerado" — pra o usuário revisar/reverter. Fica ACIMA de tudo: um gate vermelho é a primeira coisa que ele vê.

  • Gráfico único — findings por severidade: NÃO desenhe o SVG. Emita o bloco chart do spec, com uma entrada por rodada: {"kind":"chart","rounds":[{"label":"R1","p0":2,"p1":6,"p2":4,"p3":1}, …]}. Os números saem direto do return do Workflow; o programa desenha as barras empilhadas P0/P1/P2/P3 e sobrepõe a linha de severidade real (P0+P1) — a composição E a queda no mesmo lugar. Um gráfico só, porque com 4-5 rodadas dois lado a lado apertam. Por que é do programa: coordenada de barra é aritmética sobre número que você já recebeu pronto, e barra fora de escala não dá erro — dá a conclusão errada sobre retornos decrescentes, que é a única leitura pra qual este gráfico existe. Contagem em string é recusada pelo build (nada de coerção silenciosa). + tabela por rodada com as considerações DENTRO da linha (colapsável, o detail do item), NÃO um listão depois. Read-only.

  • 4 categorias de ACTIONABLE — o que sobe pro usuário deixa de ser "alerta genérico" e vira 4 seções, cada achado um feedback-item SELECIONÁVEL (os valores internos keep/change/remove ficam, mas os labels viram "✓ Vira ação" / "✏️ Ação c/ ajuste" / "✗ Descartar" — o /visual autoriza relabelar). Cada item é auto-explicável: título humano (1 linha, sem jargão de código) + porquê/impacto (1-2 linhas) + onde (path); o detalhe técnico fica no colapsável.

    • Estado inicial NEUTRO (regra dura): todo feedback-item nasce SEM seleção — sem radio checked, sem classe state-*. Nenhuma categoria entra pré-marcada como "✓ Vira ação", e isso vale especialmente pras Sugestões: o loop ter PROPOSTO um refator/drift NÃO é "sim" do usuário — é candidato a decisão dele. Razão dupla: (1) força a decisão ativa (não assume "sim" sem querer); (2) mantém o contador honesto — ele conta input:checked, então pré-marcar só a APARÊNCIA (classe state-keep sem checked) dessincroniza: o item parece selecionado e o contador segue em 0. O progresso começa em 0 de N e só sobe no clique.

    O mapeamento (a partir do return):

    1. Importantes — recomendaçãoplanFlawAlerts P0/P1 (decisões de arquitetura do plano: "apresento e julgamos").
    2. Sugestões de melhoriaplan-drift (candidatos a mudança de plano) + refators propostos pelo loop.
    3. Limitações atuais (não quebram, mas importam) ← acceptedLimits propostos + churn hotspots.
    4. Extras (opcionais) ← P2/P3 documentados (picuinhas abaixo do floor, nice-to-have).
  • Fechamento (feedback-box): progresso + observação geral + botões "Aprovar tudo" / "Copiar feedback".

Seleção live → próximo plano (o ciclo fecha aqui). O usuário marca os itens, diz "ok", e a casca lê ~/.claude/visual-state/latest.json (o daemon do /visual) e monta o próximo plano só com os itens marcados "✓ Vira ação" / "✏️ Ação c/ ajuste" (a nota do "ajuste" entra como refinamento). Copy/paste é o fallback quando o daemon está off. É assim que o relatório de QA vira o INPUT do próximo plano, sem retrabalho.

Polish: densidade controlada (sem parágrafo longo), hierarquia por cor + sev-tag, cada categoria com peso visual distinto. Linguagem humana. (Ver EXAMPLE-REPORT.html.)

Se não headless, pergunta o veredito 0-10 do usuário (1 pergunta opcional/skipável) — a âncora subjetiva.

(B) Journal AGÊNTICO — markdown verborrágico, pra "futuro-eu" (3 camadas)

NÃO é pro usuário ler — é o agente passando informação pra ele mesmo do futuro melhorar a skill. (Ver EXAMPLE-JOURNAL.md.)

  1. Telemetria quantitativa — JSONL append-only, 1 linha/sessão, pra calibrar nº de loops/rubrica. Os números saem do return do Workflow — não é contabilidade extra.
    • Por projeto: <projeto>/.claude/qa-loop/telemetry.jsonl (no .gitignore).
    • Agregado cross-projeto: ~/.claude/qa-loop/journal/telemetry.jsonl (sobrevive a reinstalar o plugin).
  2. Aprendizado da SKILL~/.claude/qa-loop/journal/learnings.md (cross-projeto, acumula): findings exaustivos sobre onde o próprio PROCESSO de QA acertou/errou + AÇÃO concreta pra futuro-eu. Ex.: "o usuário rebaixou P1→P2 num finding de naming → AÇÃO: rubrica — naming enganoso só é P1 com bug concreto"; "regressão escapou do gate porque o teste-red não cobria a interação entre estágios → o checklist do Revisor precisa de uma linha sobre interação". Registra também o que FUNCIONOU (pra não regredir o prompt).
  3. Memória de QA do PROJETO — versionada, junto da config (.claude/qa-loop.config.md): invariantes vivas, accepted-limits ratificados, churn hotspots (funções acopladas), plan-flaws recorrentes.

O loop fecha: a skill LÊ o journal no INÍCIO de cada sessão (passo 0)

A memória do projeto semeia os accepted-limits + invariantes; os aprendizados da skill afinam a rubrica/prompt. É assim que "melhora com o uso" — agêntico + humano-no-loop (os aprendizados viram refino da skill pelo agente e pelo usuário, periodicamente). Zero auto-tuning automático no v1. (Por que não autoresearch: o método do Karpathy depende de UMA métrica escalar barata e comparável entre runs, que review NÃO tem; o keep/discard dele já existe aqui no lugar certo — é o regression gate, que decide por teste objetivo.)

Schema da telemetria (1 objeto por linha):

{"ts":"<ISO>","target":"<path/desc>","domain":"asymptotic","severity_floor":"P1","max_rounds_config":6,"rounds_run":3,"corrections_per_round":[5,1,0],"last_round_with_severe_finding":2,"regressions_self_inflicted":1,"accepted_limits":1,"plan_flaw_alerts":1,"tokens_per_round":[...],"stop_reason":"no-severe-finding","user_score_0_10":null}

last_round_with_severe_finding (mediana entre sessões assintóticas) é a resposta empírica ao "3 loops?".


Guard-rails operacionais

Regression gate por conserto (o maior ROI)

A suíte inteira a cada conserto converte 4 dos 5 mecanismos de regressão de "descoberto 3 rodadas depois" em "falha na mesma edição". É o GATE de regressão do motor. Não pule, não bata só o teste novo.

Os 5 mecanismos de regressão (operacionalizados)

  • A — alargamento de escopo: consertar um falso-negativo alargando uma regra genérica cria falso-positivo em outro canto. → extensão enumerada > alargamento; negative-tests logo após. Prevenido no PLAN.
  • B — ordem de curto-circuito: exceção (early-return) antes da regra principal engole o caso de interseção. → ordene checagens positivas fortes ANTES das exceções; teste a interseção. Pego pelo gate.
  • C — proxy sintático grosseiro: uma propriedade superficial (tem "/", é numérico) usada como semântica vale pros dois lados da fronteira. → combine sinais; na dúvida, inverta o default pro lado seguro. Pego pelo gate.
  • D — conflito entre correções: a correção K+n viola a invariante da correção K. → teste nomeado com a invariante; suíte inteira a cada conserto. Prevenido no PLAN (invariantes vivas) + pego pelo gate.
  • E — interação entre estágios: um estágio deixa resíduo que outro consome. → cada estágio neutraliza o que processou; teste a INTERAÇÃO entre estágios. Pego pelo gate.

Detecção de rede — DOIS usos da mesma detecção

As camadas abaixo são detectadas UMA vez e servem a dois propósitos distintos:

  1. Regression net DURANTE o loop (Fase Assintótica) — a suíte roda a cada conserto pra pegar regressão auto-infligida na hora. Aqui "cumprir 100%" = honestidade 100% sobre a rede, nunca fingir segurança; com rede fraca, baixa o teto e declara o blast-radius não-coberto.
  2. Fase Gate de SAÍDA (Passo 8.0) — portão absoluto: só as Camadas 1 e 3 (unit/integração + lint/type) compõem o gate e têm que estar 100% verdes no repo pra declarar sucesso. A Camada 2 (caracterização, escopo parcial) e a Camada 4 (e2e, dependente de ambiente) ficam de FORA do gate — são da fase assintótica. Aqui NÃO há "teto" nem "retorno decrescente" — é binário. ("Teto" só governa o nº de rodadas da fase assintótica quando a rede é fraca; não toca o gate.)

Detecte na ordem:

  • Camada 1 — Suíte de testes (unit + integração). Detecta: test script em package.json · jest/vitest/mocha em devDependencies · pytest em pyproject.toml/setup.cfg · Cargo.tomlcargo test · go.modgo test ./... · target test em Makefile. → compõe a Fase Gate (absoluta). e2e marcado (@pytest.mark.e2e) é separado daqui → Camada 4.
  • Camada 2 — Testes-de-caracterização gerados dos próprios teste-red do gate. Cobre SÓ os findings já tocados — declara isso. Net da Fase Assintótica (não é portão absoluto). Começa vazia (rodada 1 roda com menos rede).
  • Camada 3 — Lint + typecheck. Detecta: eslint.config.*/.eslintrc.*/biome.json→lint; tsconfig.json/pyproject.toml(ruff/mypy/pyright)→typecheck. → compõe a Fase Gate (absoluta). Era "net teto 2" — virou portão: um erro de type não é net fraco, é falha binária.
  • Camada 4 — Jornada Playwright / e2e (alvo com UX). Print + análise da tela (não só DOM). Cobre só o caminho roteirizado. Fase Assintótica — vira actionable, não bloqueia (lento e dependente de ambiente). Teto 2.
  • Camada 5 — Sem rede objetiva. Fase Gate vazia, declarada honesto. A Fase Assintótica NÃO roda como se houvesse proteção ("sem testes, sem lint/types, sem jornada — cada conserto é aposta cega; rodo no máx 1 rodada de críticos e paro"). Teto 1.

A camada escolhida (e o que ela não cobre) é arg do Workflow (safetyLayer), entra no header da rodada e no relatório. Os tetos das Camadas 4 e 5 não são conselho ao modelo — o motor clampa maxRounds por LAYER_CAP, então rede fraca não consegue gastar 6 rodadas de aposta cega nem que o prompt esqueça a regra.

Churn detector

Por FUNÇÃO (não arquivo): conta edições + regressões auto-infligidas. ≥churn_threshold regressões na mesma função → escala (para de remendar; sinaliza "acoplamento alto: refatora pra detectores independentes OU aceita os limites"). É lógica do script do motor.

Accepted-limits (R6) — proposto, nunca enterrado sozinho

Vivo, ancorado ao alvo. O loop PROPÕE (plan.proposedLimits); o Planejador adjudica se procede; mas só vira PERMANENTE quando o usuário move pra .claude/qa-loop.config.md. Critério explícito de entrada (é genuinamente espaço-de-input-infinito? ou só difícil?) — não é o lixo do que cansou de consertar; um bug real classificado como limite some pra sempre. Headless propõe + reporta, nunca grava permanente.

Finding de Fase Gate (lint/type/unit/integração vermelho) é INELEGÍVEL a accepted-limit — erro objetivo nunca vira "limite aceito" nem P2 rebaixado. Só findings da Fase Assintótica podem virar limite; o gate é absoluto por definição.

Findings arquiteturais (não viram teste-red)

"A abordagem é frágil", "acoplamento alto", "naming enganoso" não reproduzem por teste determinístico. → excluídos do regression gate; só reportados (viram bucket plan-flaw / alerta, ou P2 documentado). Nunca consertados automaticamente sem rede.


Modo headless (pro /sovai)

Com --headless: o loop NUNCA pergunta nada (auto-classifica domínio como assintótico se ambíguo, usa os defaults, pula o veredito 0-10). Alertas de plan-flaw continuam NÃO virando fix — ficam no relatório pro usuário revisar depois. Headless ≠ licença pra re-planejar nem pra ratificar accepted-limit. O relatório da /qa-loop vira a seção final de QA do report do /sovai.

A Fase Gate (absoluta) vale em headless também — o gate vermelho não pergunta nada, mas entra no relatório como bloqueante (stopReason='gate-red'), virando item de "Bloqueios (precisam de você)" no report do /sovai. Conserto de fundamento (lint/type/unit/integração) está no mandato do headless; re-planejar um plan-flaw, não.

Quando NÃO usar

  • Sem nada implementado pra revisar → recuse.
  • Aplicar fix sem rede de regressão E sem aceitar o teto-1 da Camada 5 → declare a cegueira, não finja.
  • Tarefa puramente convergente de 1 passo que você já sabe consertar → só conserte, não embrulhe num loop.

Regras de segurança

  • Nunca declarar "rodada limpa" sem complete=true (todos os ângulos do checklist cobertos).
  • Nunca declarar SUCESSO com a Fase Gate vermelha. lint/type/unit/integração do repo têm que estar 100% verdes (incluindo pré-existentes); se travar, stopReason='gate-red', nunca "sucesso". Erro objetivo nunca vira accepted-limit nem P2 rebaixado.
  • Fase Gate é repo-inteiro-absoluto, mas com transparência: o relatório separa "conserto da revisão" de "débito pré-existente zerado" (com diff) — conserto repo-wide nunca é silencioso (respeita surgical-changes).
  • Nunca consertar um finding de bucket plan-drift no sentido que afasta do plano; nunca implementar um plan-flaw.
  • Nunca rodar como se houvesse rede de regressão quando não há (Camada 5 é declaração explícita, não fallback silencioso).
  • O teto é trava, não meta — não pare só porque "rodou N vezes" se ainda há severidade real.
  • Quem decide keep/revert de um fix é o script + o Planejador, nunca o agente que fez o fix — mesmo com todo mundo em Opus 5, o gate é separação de papel, não desconfiança de modelo.

What ships with it: 6 files

56.6 KB alongside SKILL.md, 1 of them executable

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.