bigpowers 2.89.0 → 2.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/scripts/ab-sandbox.sh +85 -0
- package/scripts/bp-churn-rank.sh +3 -2
- package/scripts/check-spec-version-gap.sh +2 -1
- package/scripts/land-branch.sh +7 -3
- package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_okf.cpython-310.pyc +0 -0
- package/scripts/lib/ab-arm-bigpowers.sh +42 -0
- package/scripts/lib/ab-eval.py +285 -0
- package/scripts/lib/ab-sandbox/agent.Dockerfile +33 -0
- package/scripts/lib/ab-sandbox/filter +6 -0
- package/scripts/lib/ab-sandbox/proxy.Dockerfile +13 -0
- package/scripts/lib/ab-sandbox/tinyproxy.conf +19 -0
- package/scripts/lib/ab_metrics.py +123 -0
- package/scripts/lib/ab_progress.py +103 -0
- package/scripts/lib/ab_report.py +116 -0
- package/scripts/lib/golden-suite-gates.sh +2 -0
- package/scripts/lib/parallel-review-worktrees.sh +3 -1
- package/scripts/lib/plan-consistency-check.sh +4 -2
- package/scripts/lib/run-benchmark.py +30 -16
- package/scripts/run-ab-eval.sh +7 -0
- package/scripts/run-smoke.sh +3 -2
- package/scripts/run-story-verify.sh +7 -0
- package/scripts/test-pipefail-pipe-race.sh +68 -0
- package/scripts/validate-doctrine.sh +3 -3
- package/tests/test_ab_ablation.py +91 -0
- package/tests/test_ab_eval.py +309 -0
- package/tests/test_ab_pi.py +63 -0
- package/tests/test_ab_progress.py +81 -0
- package/tests/test_ab_sandbox.py +94 -0
- package/tests/test_ab_score.py +113 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# story: e58s03
|
|
3
|
+
"""tests/test_ab_score.py — partial-credit scoring and the hard tasks (ab-11, ab-13).
|
|
4
|
+
|
|
5
|
+
Run: python3 tests/test_ab_score.py
|
|
6
|
+
Split from test_ab_eval.py (300-line cap); reuses its Sandbox and module loaders.
|
|
7
|
+
"""
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import tempfile
|
|
12
|
+
import unittest
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
|
|
17
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
18
|
+
from test_ab_eval import REPO_ROOT, Sandbox, met, rep # noqa: E402
|
|
19
|
+
|
|
20
|
+
TASKS = REPO_ROOT / "specs" / "benchmarks" / "ab" / "tasks"
|
|
21
|
+
HARD_TASKS = ["ab-11-ledger-rounding", "ab-13-orders-split"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestTapScore(unittest.TestCase):
|
|
25
|
+
def test_share_of_passing_tests(self):
|
|
26
|
+
self.assertEqual(met.tap_score("# tests 4\n# pass 3\n# fail 1\n"), 0.75)
|
|
27
|
+
|
|
28
|
+
def test_several_summaries_are_summed(self):
|
|
29
|
+
self.assertEqual(met.tap_score("# pass 2\n# fail 0\nnoise\n# pass 1\n# fail 1\n"), 0.75)
|
|
30
|
+
|
|
31
|
+
def test_no_summary_is_none(self):
|
|
32
|
+
self.assertIsNone(met.tap_score("SyntaxError: boom\n"))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestScoreInRuns(Sandbox):
|
|
36
|
+
def set_score(self, command):
|
|
37
|
+
task_yaml = self.tasks_dir / "t-01" / "task.yaml"
|
|
38
|
+
task = yaml.safe_load(task_yaml.read_text())
|
|
39
|
+
task["score"] = command
|
|
40
|
+
task_yaml.write_text(yaml.safe_dump(task))
|
|
41
|
+
|
|
42
|
+
def test_score_command_sets_score(self):
|
|
43
|
+
self.set_score("printf '# pass 3\\n# fail 1\\n'; exit 1")
|
|
44
|
+
self.run_main("--adapter-cmd", "echo 42 > answer.txt", "--arms", "vanilla")
|
|
45
|
+
self.assertEqual(self.records()[0]["score"], 0.75)
|
|
46
|
+
|
|
47
|
+
def test_no_score_key_means_null(self):
|
|
48
|
+
self.run_main("--adapter-cmd", "echo 42 > answer.txt")
|
|
49
|
+
self.assertIsNone(self.records()[0]["score"])
|
|
50
|
+
report = yaml.safe_load(next(self.out.glob("AB-*.yaml")).read_text())
|
|
51
|
+
self.assertIsNone(report["delta"].get("score"))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestScoreInReport(unittest.TestCase):
|
|
55
|
+
def rec(self, arm, task, score):
|
|
56
|
+
return {"arm": arm, "task": task, "pass": score == 1.0, "tampered": False, "agent_error": False,
|
|
57
|
+
"cost_usd": None, "input_tokens": None, "output_tokens": None, "turns": None,
|
|
58
|
+
"duration_s": 1.0, "diff_lines": 1, "score": score}
|
|
59
|
+
|
|
60
|
+
def test_mean_score_and_delta(self):
|
|
61
|
+
records = [self.rec("vanilla", "t", 0.5), self.rec("vanilla", "t", 0.7),
|
|
62
|
+
self.rec("bigpowers", "t", 0.9), self.rec("bigpowers", "t", 1.0)]
|
|
63
|
+
result = rep.aggregate(records, ["vanilla", "bigpowers"], ["t"])
|
|
64
|
+
self.assertEqual(result["arms_summary"]["vanilla"]["mean_score"], 0.6)
|
|
65
|
+
self.assertEqual(result["per_task_score"]["t"]["bigpowers"], 0.95)
|
|
66
|
+
self.assertEqual(result["delta"]["score"], 0.35)
|
|
67
|
+
lo, hi = result["delta"]["score_ci95"]
|
|
68
|
+
self.assertLessEqual(lo, 0.35)
|
|
69
|
+
self.assertGreaterEqual(hi, 0.35)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@unittest.skipUnless(shutil.which("node"), "node is needed for the hard tasks")
|
|
73
|
+
class TestHardTasks(unittest.TestCase):
|
|
74
|
+
"""Each hard task must fail as given, pass with its solution, and give partial credit."""
|
|
75
|
+
|
|
76
|
+
def grade(self, name: str, with_solution: bool) -> tuple[bool, float | None]:
|
|
77
|
+
task_dir = TASKS / name
|
|
78
|
+
task = yaml.safe_load((task_dir / "task.yaml").read_text())
|
|
79
|
+
work = Path(tempfile.mkdtemp(prefix="ab-hard-"))
|
|
80
|
+
try:
|
|
81
|
+
shutil.copytree(task_dir / "fixture", work, dirs_exist_ok=True)
|
|
82
|
+
if with_solution:
|
|
83
|
+
shutil.copytree(task_dir / "solution", work, dirs_exist_ok=True)
|
|
84
|
+
shutil.copytree(task_dir / "hidden", work, dirs_exist_ok=True)
|
|
85
|
+
run = lambda cmd: subprocess.run(["bash", "-c", cmd], cwd=work, capture_output=True, text=True)
|
|
86
|
+
passed = run(task["grade"]).returncode == 0
|
|
87
|
+
return passed, met.tap_score(run(task["score"]).stdout)
|
|
88
|
+
finally:
|
|
89
|
+
shutil.rmtree(work, ignore_errors=True)
|
|
90
|
+
|
|
91
|
+
def test_fixture_fails_with_partial_score(self):
|
|
92
|
+
for name in HARD_TASKS:
|
|
93
|
+
with self.subTest(task=name):
|
|
94
|
+
passed, score = self.grade(name, with_solution=False)
|
|
95
|
+
self.assertFalse(passed)
|
|
96
|
+
assert score is not None
|
|
97
|
+
self.assertLess(score, 1.0)
|
|
98
|
+
|
|
99
|
+
def test_solution_passes_with_full_score(self):
|
|
100
|
+
for name in HARD_TASKS:
|
|
101
|
+
with self.subTest(task=name):
|
|
102
|
+
self.assertEqual(self.grade(name, with_solution=True), (True, 1.0))
|
|
103
|
+
|
|
104
|
+
def test_score_runs_hidden_tests_only(self):
|
|
105
|
+
for name in HARD_TASKS:
|
|
106
|
+
with self.subTest(task=name):
|
|
107
|
+
score_cmd = yaml.safe_load((TASKS / name / "task.yaml").read_text())["score"]
|
|
108
|
+
self.assertIn("test-hidden", score_cmd)
|
|
109
|
+
self.assertNotRegex(score_cmd, r"(^|\s)test/")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
unittest.main(verbosity=1)
|