vajeeda commited on
Commit
7414383
Β·
1 Parent(s): fea7356

feat(phase0): add run_critic_gate CLI with rich output and fixture saving

Browse files
viral_script_engine/scripts/run_critic_gate.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from dotenv import load_dotenv
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich import box
11
+ from rich.panel import Panel
12
+
13
+ load_dotenv()
14
+
15
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
16
+
17
+ from viral_script_engine.agents.critic import CriticAgent, CriticParseError
18
+ from viral_script_engine.evaluation.critic_evaluator import CriticEvaluator
19
+
20
+ console = Console()
21
+ BASE_DIR = Path(__file__).parent.parent
22
+
23
+
24
+ def load_scripts(dry_run: bool) -> list:
25
+ scripts_path = BASE_DIR / "data" / "test_scripts" / "scripts.json"
26
+ with open(scripts_path) as f:
27
+ scripts = json.load(f)
28
+ if dry_run:
29
+ scripts = scripts[:2]
30
+ return scripts
31
+
32
+
33
+ def run_gate(max_retries: int = 3, dry_run: bool = False) -> bool:
34
+ agent = CriticAgent()
35
+ evaluator = CriticEvaluator()
36
+ scripts = load_scripts(dry_run)
37
+
38
+ table = Table(title="Critic Gate Results", box=box.ROUNDED)
39
+ table.add_column("Script ID", style="cyan", no_wrap=True)
40
+ table.add_column("Claims", justify="center")
41
+ table.add_column("Specificity", justify="center")
42
+ table.add_column("Falsifiability", justify="center")
43
+ table.add_column("Gate", justify="center")
44
+
45
+ outputs = []
46
+ all_results = []
47
+
48
+ with console.status("[bold green]Running CriticAgent on scripts...") as status:
49
+ for entry in scripts:
50
+ sid = entry["script_id"]
51
+ status.update(f"[bold green]Processing {sid}...")
52
+
53
+ critique_output = None
54
+ for attempt in range(max_retries):
55
+ try:
56
+ critique_output = agent.critique(
57
+ script=entry["script_text"],
58
+ region=entry["region"],
59
+ platform=entry["platform"],
60
+ niche=entry["niche"],
61
+ )
62
+ break
63
+ except CriticParseError as e:
64
+ if attempt == max_retries - 1:
65
+ console.print(f"[red]FAILED {sid} after {max_retries} attempts: {e}")
66
+ else:
67
+ console.print(f"[yellow]Retry {attempt + 1} for {sid}")
68
+
69
+ if critique_output is None:
70
+ continue
71
+
72
+ result = evaluator.evaluate(critique_output, entry["script_text"], script_id=sid)
73
+ all_results.append(result)
74
+ outputs.append((sid, entry, critique_output))
75
+
76
+ gate_str = "[green]PASS[/green]" if result.passes_gate else "[red]FAIL[/red]"
77
+ table.add_row(
78
+ sid,
79
+ str(result.claim_count),
80
+ f"{result.specificity_score:.2f}",
81
+ f"{result.falsifiability_score:.2f}",
82
+ gate_str,
83
+ )
84
+
85
+ console.print(table)
86
+
87
+ pass_count = sum(1 for r in all_results if r.passes_gate)
88
+ pass_rate = pass_count / len(all_results) if all_results else 0.0
89
+ overall_pass = pass_rate >= 0.8
90
+
91
+ if overall_pass:
92
+ fixtures_dir = BASE_DIR / "data" / "golden_fixtures"
93
+ fixtures_dir.mkdir(exist_ok=True)
94
+ for sid, entry, critique_output in outputs:
95
+ fixture_path = fixtures_dir / f"fixture_{sid}.json"
96
+ fixture_data = {
97
+ "script_id": sid,
98
+ "region": entry["region"],
99
+ "platform": entry["platform"],
100
+ "niche": entry["niche"],
101
+ "critique": critique_output.model_dump(),
102
+ }
103
+ with open(fixture_path, "w") as f:
104
+ json.dump(fixture_data, f, indent=2)
105
+ console.print(f"[green]Golden fixtures saved to {fixtures_dir}")
106
+
107
+ failing = [r.script_id for r in all_results if not r.passes_gate]
108
+ if failing:
109
+ console.print(f"[red]Failing scripts: {', '.join(failing)}")
110
+ for r in all_results:
111
+ if not r.passes_gate:
112
+ console.print(
113
+ f" [yellow]{r.script_id}[/yellow]: "
114
+ f"claims={r.claim_count}, specificity={r.specificity_score:.2f}, "
115
+ f"falsifiability={r.falsifiability_score:.2f}"
116
+ )
117
+
118
+ gate_label = f"PHASE 0 GATE: {'PASS' if overall_pass else 'FAIL'}"
119
+ style = "bold green" if overall_pass else "bold red"
120
+ console.print(Panel(f"[{style}]{gate_label}[/{style}] ({pass_count}/{len(all_results)} scripts passed)"))
121
+
122
+ return overall_pass
123
+
124
+
125
+ def main():
126
+ parser = argparse.ArgumentParser(description="Run Critic quality gate")
127
+ parser.add_argument("--max-retries", type=int, default=3)
128
+ parser.add_argument("--dry-run", action="store_true")
129
+ args = parser.parse_args()
130
+
131
+ passed = run_gate(max_retries=args.max_retries, dry_run=args.dry_run)
132
+ sys.exit(0 if passed else 1)
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main()
viral_script_engine/tests/test_critic.py CHANGED
@@ -87,3 +87,24 @@ def test_evaluator_fails_low_specificity():
87
  evaluator = CriticEvaluator()
88
  result = evaluator.evaluate(output, SCRIPT_TEXT)
89
  assert result.passes_gate is False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  evaluator = CriticEvaluator()
88
  result = evaluator.evaluate(output, SCRIPT_TEXT)
89
  assert result.passes_gate is False
90
+
91
+
92
+ # ── Task 5: CLI exit-code test ────────────────────────────────────────────────
93
+
94
+ import os
95
+ import subprocess
96
+ import sys
97
+
98
+
99
+ def test_cli_dry_run_exits_zero_or_one():
100
+ """CLI must exit 0 (pass) or 1 (fail) β€” never crash with unhandled exception."""
101
+ result = subprocess.run(
102
+ [sys.executable, "scripts/run_critic_gate.py", "--dry-run"],
103
+ capture_output=True,
104
+ text=True,
105
+ cwd=str(__import__("pathlib").Path(__file__).parent.parent),
106
+ env={**os.environ, "ANTHROPIC_API_KEY": "sk-fake-key-for-test"},
107
+ )
108
+ assert result.returncode in (0, 1), (
109
+ f"Unexpected exit code: {result.returncode}\nSTDERR: {result.stderr}"
110
+ )