Files
dev-workflows/qa/test_quality.py
T
Evandro Fonseca Junior 722c544b72 feat: add dev workflows as agent skills
Four SKILL.md-based workflows for agent-driven development: frontend/UI,
backend/API, architecture, and security code review.

Each workflow defines phases with verification steps, acceptance criteria
and hard rules so agents deliver work that can be checked instead of
asserted. The security review consults live sources (OSV.dev, GitHub
Advisory, NVD) because model CVE knowledge is frozen at training cutoff.

Includes a QA harness (qa/) that scores workflows on 30+ criteria, a
non-regression gate, and meta-tests that corrupt the harness to prove it
detects regressions.
2026-08-19 05:22:09 -03:00

95 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
test_quality.py — testes validados + aprovados (gate de qualidade não-regressivo).
Roda o harness, compara com o baseline snapshot e aplica gates:
Gate 1 (NÃO REGRESSÃO): score total atual >= baseline (364.1). Se caiu, exit 1.
Gate 2 (MELHORIA ≥2×): score total atual >= 2 * baseline (728.2). Meta do usuário.
Gate 3 (POR WORKFLOW): cada um dos 3 workflows >= 2 * seu baseline individual.
Gate 4 (SKILLS): nenhuma skill referenciada faltando (harness sem sem_match).
Gate 5 (VERIFICAÇÃO POR FASE): toda fase tem >=3 verbos de verificação.
Uso: python3 test_quality.py -> exit 0 se G1,G3,G4,G5 ok (evidência)
python3 test_quality.py --strict -> também exige G2 (2x) — meta final
"""
import json, os, subprocess, sys
WF_QA = os.path.dirname(os.path.abspath(__file__))
HARNESS = os.path.join(WF_QA, "wf_quality_harness.py")
SNAPSHOT = os.path.join(WF_QA, "snapshots", "baseline-final.json")
def run_harness():
r = subprocess.run([sys.executable, HARNESS, "--json"], capture_output=True, text=True)
if r.returncode != 0:
return None, r.stderr
return json.loads(r.stdout), None
def main():
strict = "--strict" in sys.argv
base = json.load(open(SNAPSHOT))
base_total = base["total"]
base_per = {w["name"]: w.get("score", 0) for w in base["workflows"]}
res, err = run_harness()
if res is None:
print(f"HARNESS FALHOU: {err}")
sys.exit(1)
cur_total = res["total"]
cur_per = {w["name"]: w.get("score", 0) for w in res["workflows"]}
sem_match = {w["name"]: w.get("skills_sem_match", []) for w in res["workflows"]}
fases_vazias = {w["name"]: w.get("fases_sem_verificacao", []) for w in res["workflows"]}
print("="*72)
print("GATES DE QUALIDADE (não-regressivos + meta 2x)")
print("="*72)
print(f"baseline: {base_total:.1f} | atual: {cur_total:.1f} | meta(2x): {2*base_total:.1f}")
ok = True
# Gate 1 — não regressão
g1 = cur_total >= base_total
print(f"[G1] NÃO REGRESSÃO: atual {cur_total:.1f} >= baseline {base_total:.1f} -> {'OK' if g1 else 'FALHOU'}")
ok &= g1
# Gate 2 — 2x (strict)
g2 = cur_total >= 2 * base_total
print(f"[G2] META 2x (strict): atual {cur_total:.1f} >= {2*base_total:.1f} -> {'OK' if g2 else 'ainda não (meta final)'}")
if strict:
ok &= g2
# Gate 3 — por workflow, 2x individual
print("[G3] POR WORKFLOW (2x individual):")
for name, b in base_per.items():
cur = cur_per.get(name, 0)
g3i = cur >= 2 * max(b, 0.1)
print(f" {name:18} base={b:6.1f} atual={cur:6.1f} 2x={2*max(b,0.1):6.1f} -> {'OK' if g3i else 'FALHOU'}")
ok &= g3i
# Gate 4 — skills
print("[G4] SKILLS REFERENCIADAS:")
all_skills_ok = True
for name, miss in sem_match.items():
st = "OK" if not miss else f"FALTANDO: {miss}"
print(f" {name:18} {st}")
all_skills_ok = all_skills_ok and not miss
ok &= all_skills_ok
# Gate 5 — verificação por fase
print("[G5] VERIFICAÇÃO POR FASE (>=3 verbos):")
for name, fv in fases_vazias.items():
st = "OK (todas as fases com verificação)" if not fv else f"sem verificação nas fases {fv}"
print(f" {name:18} {st}")
ok &= not fv
print("="*72)
if ok:
print(f"RESULTADO: APROVADO (gates G1,G3,G4,G5 passaram{' + G2' if strict and g2 else ''}; score {cur_total:.1f})")
sys.exit(0)
else:
print(f"RESULTADO: REPROVADO — gates abaixo falharam. Score {cur_total:.1f}. Rodar refinamento de novo.")
sys.exit(1)
if __name__ == "__main__":
main()