chore: bootstrap agnostic agent harness framework
This commit is contained in:
137
scripts/verify.sh
Executable file
137
scripts/verify.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
ok() { printf "${GREEN}[OK]${NC} %s\n" "$1"; }
|
||||
warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; }
|
||||
fail() { printf "${RED}[FAIL]${NC} %s\n" "$1"; }
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
echo "── 1) Verificando estructura base ─────────────────────"
|
||||
required=(
|
||||
"AGENTS.md"
|
||||
"CHECKPOINTS.md"
|
||||
"harness/agents.matrix.yml"
|
||||
"harness/workflow.stages.yml"
|
||||
"harness/policies/governance.md"
|
||||
"harness/policies/security.md"
|
||||
"harness/policies/quality.md"
|
||||
"harness/contracts/handoff.md"
|
||||
"harness/contracts/evidence.schema.json"
|
||||
"spec/product.md"
|
||||
"spec/tech.md"
|
||||
"spec/acceptance.md"
|
||||
"backlog/features.json"
|
||||
"work/current.md"
|
||||
"work/history.md"
|
||||
)
|
||||
|
||||
for f in "${required[@]}"; do
|
||||
if [ -f "$f" ]; then
|
||||
ok "Existe $f"
|
||||
else
|
||||
fail "Falta $f"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "── 2) Validando backlog + gates ───────────────────────"
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
root = pathlib.Path('.')
|
||||
path = root / 'backlog' / 'features.json'
|
||||
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding='utf-8'))
|
||||
except Exception as e:
|
||||
print(f"[FAIL] backlog/features.json inválido: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
valid = set(data.get('rules', {}).get('valid_status', ["pending", "in_progress", "blocked", "done"]))
|
||||
features = data.get('features', [])
|
||||
if not isinstance(features, list):
|
||||
print('[FAIL] features debe ser una lista')
|
||||
sys.exit(1)
|
||||
|
||||
in_progress = [f for f in features if f.get('status') == 'in_progress']
|
||||
if len(in_progress) > 1:
|
||||
print(f"[FAIL] Hay {len(in_progress)} features in_progress (máximo 1)")
|
||||
sys.exit(1)
|
||||
|
||||
for f in features:
|
||||
fid = str(f.get('id', '')).strip()
|
||||
status = f.get('status')
|
||||
if status not in valid:
|
||||
print(f"[FAIL] Estado inválido en feature {fid}: {status}")
|
||||
sys.exit(1)
|
||||
|
||||
if status == 'done':
|
||||
d = root / 'work' / 'artifacts' / fid
|
||||
req = ['reviewer.json', 'security.json', 'qa.json', 'leader-close.json']
|
||||
missing = [name for name in req if not (d / name).is_file()]
|
||||
if missing:
|
||||
print(f"[FAIL] Feature {fid} done sin artefactos: {', '.join(missing)}")
|
||||
sys.exit(1)
|
||||
|
||||
expected = {
|
||||
'reviewer.json': 'reviewer',
|
||||
'security.json': 'security',
|
||||
'qa.json': 'qa',
|
||||
'leader-close.json': 'leader',
|
||||
}
|
||||
for filename, agent in expected.items():
|
||||
try:
|
||||
obj = json.loads((d / filename).read_text(encoding='utf-8'))
|
||||
except Exception as e:
|
||||
print(f"[FAIL] {fid}/{filename} inválido: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if obj.get('agent') != agent:
|
||||
print(f"[FAIL] {fid}/{filename} agent debe ser '{agent}'")
|
||||
sys.exit(1)
|
||||
if obj.get('verdict') != 'APPROVED':
|
||||
print(f"[FAIL] {fid}/{filename} no está APPROVED")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[OK] backlog válido ({len(features)} features)")
|
||||
PY
|
||||
if [ $? -ne 0 ]; then EXIT_CODE=1; fi
|
||||
|
||||
echo ""
|
||||
echo "── 3) Verificación de tests/build (opcional auto-detect) ─"
|
||||
if [ -f "Makefile" ] && grep -qE '^test:' Makefile; then
|
||||
if make test; then ok "make test OK"; else fail "make test falló"; EXIT_CODE=1; fi
|
||||
elif [ -f "package.json" ]; then
|
||||
if command -v npm >/dev/null 2>&1; then
|
||||
if npm test --silent --if-present; then ok "npm test OK"; else fail "npm test falló"; EXIT_CODE=1; fi
|
||||
else
|
||||
warn "package.json detectado pero npm no está disponible"
|
||||
fi
|
||||
elif [ -d "tests" ]; then
|
||||
if command -v pytest >/dev/null 2>&1; then
|
||||
if pytest -q; then ok "pytest OK"; else fail "pytest falló"; EXIT_CODE=1; fi
|
||||
else
|
||||
if python3 -m unittest discover -s tests -v; then ok "unittest OK"; else fail "unittest falló"; EXIT_CODE=1; fi
|
||||
fi
|
||||
else
|
||||
warn "No se detectó suite automática (tests/ | Makefile test | package.json test)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "── 4) Resumen ─────────────────────────────────────────"
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
ok "Harness verificado. Puedes trabajar."
|
||||
else
|
||||
fail "Harness NO verificado. Corrige antes de continuar."
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
Reference in New Issue
Block a user