system-prompts-and-models-o.../salesflow-saas/scripts/v005_truth_registry_audit.py
Claude 3ef62652aa
Phase 2 Execution Waves: 90-day plan + Verification Protocol scaffolding
Saves the DEALIX_PHASE2_EXECUTION_WAVES.md 90-day plan and scaffolds every
artifact the coding agent can produce. Wave A-E execution is explicitly
blocked until the Week-12 Phase Gate (§3) returns Green.

Added:
  §1 Verification Protocol (V001-V007)
    - scripts/v001_secret_scan.sh — trufflehog + gitleaks full-history scan
    - backend/tests/security/test_rls_fuzz.py — 10K cross-tenant fuzz
    - docs/verification/V003_pentest_engagement.md — vendor RFP + scope
    - docs/verification/V004_no_founder_demo_test.md — 3-tester protocol
    - scripts/v005_truth_registry_audit.py — independent audit tool
    - infra/load-tests/baseline.js — k6 perf baseline
    - frontend/tests/a11y/baseline.spec.ts — Playwright+axe baseline
    - docs/baselines/README.md + docs/verification/README.md

  §2 Founder Decision Sprint (FD001-FD005)
    - docs/internal/legal_entity_decision.md — MISA/DIFC/Delaware brief
    - docs/internal/trademark_status.md — SAIP filing kit tracker
    - docs/hiring/{design_engineer, backend_engineer, head_of_cs}.md

  §3 Customer Validation (CV001-CV004)
    - docs/customer_learnings/pilot_agreement_template.md
    - docs/customer_learnings/pilot_template/success_criteria.md
    - docs/customer_learnings/pilot_template/kickoff_checklist.md
    - docs/customer_learnings/friction_log.md + feature_requests.yaml
    - docs/customer_learnings/weekly_review_template.md

  Truth registry updates
    - docs/registry/TRUTH.yaml — new verification_protocol,
      founder_decision_sprint, customer_validation sections

Gates (post-change):
  architecture_brief.py     40/40
  release_readiness_matrix  94/94 (added 30 new scaffold checks)
  v005_truth_registry_audit 19/19 SUPPORTED
2026-04-17 11:13:27 +00:00

152 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
"""V005 — Truth Registry Independent Audit.
Audits every claim in TRUTH.yaml + claims_registry.yaml against live code.
Meant to be run by an engineer who did NOT author the registry.
Verdicts:
SUPPORTED — evidence file exists AND contains expected marker
UNSUPPORTED — evidence missing or stale
AMBIGUOUS — evidence exists but cannot verify intent automatically
Any UNSUPPORTED claim must be either:
(a) remediated with evidence within 48h, OR
(b) demoted to `status: roadmap` within 48h
Usage:
python scripts/v005_truth_registry_audit.py
python scripts/v005_truth_registry_audit.py --strict # fail on AMBIGUOUS
Exit codes:
0 = all SUPPORTED
1 = UNSUPPORTED claims present
2 = AMBIGUOUS claims present (with --strict)
"""
from __future__ import annotations
import argparse
import json
import sys
from dataclasses import dataclass
from pathlib import Path
import yaml
ROOT = Path(__file__).resolve().parent.parent
TRUTH_PATH = ROOT / "docs" / "registry" / "TRUTH.yaml"
CLAIMS_PATH = ROOT / "commercial" / "claims_registry.yaml"
@dataclass
class AuditResult:
claim_id: str
claim_name: str
status: str
evidence_path: str | None
verdict: str
reason: str
def audit_capability(cap: dict) -> AuditResult:
cid = cap.get("id", "?")
name = cap.get("name", "?")
status = cap.get("status", "?")
ev_path_str = cap.get("evidence_path")
public_allowed = cap.get("public_claim_allowed", False)
if status == "roadmap":
return AuditResult(cid, name, status, ev_path_str, "SUPPORTED",
"declared roadmap; no evidence required")
if not ev_path_str:
verdict = "UNSUPPORTED" if public_allowed else "AMBIGUOUS"
return AuditResult(cid, name, status, None, verdict,
"status claims progress but evidence_path is null")
ev_path = ROOT / ev_path_str
if not ev_path.exists():
return AuditResult(cid, name, status, ev_path_str, "UNSUPPORTED",
f"evidence file missing: {ev_path_str}")
if status == "live" and public_allowed:
if ev_path.is_file():
content = ev_path.read_text(errors="ignore")
if len(content.strip()) < 40:
return AuditResult(cid, name, status, ev_path_str, "AMBIGUOUS",
"evidence file exists but suspiciously empty")
return AuditResult(cid, name, status, ev_path_str, "SUPPORTED",
"evidence file present")
if status == "partial":
return AuditResult(cid, name, status, ev_path_str, "SUPPORTED",
"declared partial; evidence present")
return AuditResult(cid, name, status, ev_path_str, "AMBIGUOUS",
f"unrecognized status={status}")
def audit_registry() -> list[AuditResult]:
results: list[AuditResult] = []
if not TRUTH_PATH.exists():
print(f"ERROR: TRUTH.yaml not found at {TRUTH_PATH}", file=sys.stderr)
sys.exit(3)
truth = yaml.safe_load(TRUTH_PATH.read_text())
for cap in truth.get("capabilities", []):
results.append(audit_capability(cap))
for cap in truth.get("phase_2_capabilities", []):
results.append(audit_capability(cap))
return results
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--strict", action="store_true",
help="Fail on AMBIGUOUS verdicts")
parser.add_argument("--json", action="store_true",
help="Output JSON")
args = parser.parse_args()
results = audit_registry()
supported = [r for r in results if r.verdict == "SUPPORTED"]
unsupported = [r for r in results if r.verdict == "UNSUPPORTED"]
ambiguous = [r for r in results if r.verdict == "AMBIGUOUS"]
if args.json:
out = {
"supported": [r.__dict__ for r in supported],
"unsupported": [r.__dict__ for r in unsupported],
"ambiguous": [r.__dict__ for r in ambiguous],
"total": len(results),
}
print(json.dumps(out, indent=2))
else:
print("=" * 70)
print(" V005 — TRUTH REGISTRY INDEPENDENT AUDIT")
print("=" * 70)
print()
for r in results:
mark = {"SUPPORTED": "+", "UNSUPPORTED": "-", "AMBIGUOUS": "?"}[r.verdict]
print(f" {mark} [{r.verdict}] {r.claim_id} ({r.status}) — {r.reason}")
print()
print("-" * 70)
print(f" SUPPORTED: {len(supported)}")
print(f" UNSUPPORTED: {len(unsupported)}")
print(f" AMBIGUOUS: {len(ambiguous)}")
print("=" * 70)
if unsupported:
sys.exit(1)
if ambiguous and args.strict:
sys.exit(2)
sys.exit(0)
if __name__ == "__main__":
main()