Skip to content

Commit 847601e

Browse files
committed
Add Python policy scenario benchmark
1 parent 5078816 commit 847601e

7 files changed

Lines changed: 142 additions & 30 deletions

File tree

‎tests/fixtures/scenario_benchmarks/json_boundary_v1/benchmark.toml‎

Lines changed: 0 additions & 12 deletions
This file was deleted.

‎tests/fixtures/scenario_benchmarks/json_boundary_v1/scenario.toml‎

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
harness = "pytest"
2+
test = "tests/unit/harness/test_software_criterion_snapshots.py"
3+
snapshot = "software_criteria/control_flow_v1"
4+
target_total = "25ms"
5+
max_total = "100ms"
6+
observed_total = "19ms"
7+
regression_budget = "20ms"
8+
memory_budget_bytes = 16777216
9+
observed_memory_bytes = 8388608
10+
target_rationale = "Python control-flow policy fixtures should stay small enough for snapshot-level agent quality gates."
11+
12+
[observed_timings]
13+
parse = "6ms"
14+
policy = "9ms"
15+
snapshot = "4ms"
16+
17+
[input_expected_comparison]
18+
input_total = "19ms"
19+
expected_total = "8ms"
20+
input_memory_bytes = 8388608
21+
expected_memory_bytes = 6291456
22+
interpretation = "expected fixture exposes branch shape and native transforms before agent edits; observed policy receipts catch five low-quality input patterns."
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
id = "python-control-flow-v1"
2+
title = "Python control-flow and native-idiom findings are reproducible"
3+
policy_ids = ["PYTHON-AGENT-CONTROL-FLOW-001", "PYTHON-AGENT-NATIVE-IDIOM-001"]
4+
agent_goal = "Force agents to split nested public algorithms and native-idiom boilerplate before editing Python behavior."
5+
reference_repositories = ["python/cpython"]
6+
reference_patterns = [
7+
"Public algorithm shape is exposed through guard clauses or table dispatch",
8+
"Native transformations use comprehensions, generator expressions, or named helpers instead of manual loops",
9+
]
10+
inputs = "inputs"
11+
expected = "expect"
12+
13+
[policy_trigger]
14+
kind = "software-criterion"
15+
trigger = "pytest software criterion snapshot reads inputs/criterion.py and compares expect/findings.json plus expect/rendered.txt"
16+
evidence = "expect/findings.json"
17+
expected_rule_ids = ["PY-AGENT-POLICY-009", "PY-AGENT-POLICY-010", "PY-AGENT-POLICY-011"]
18+
expected_criteria = [
19+
"control-flow.decision-stack",
20+
"control-flow.literal-dispatch-chain",
21+
"control-flow.traversal-knot",
22+
"control-flow.broad-linear-phase",
23+
"native-idiom.manual-transform-loop",
24+
]
25+
agent_failure_mode = "AI agent leaves nested decision stacks, broad phases, and manual transform loops in place, producing redundant low-quality edits."
26+
expected_resolution = "Split the broad algorithm into named helpers, expose dispatch explicitly, and replace manual transforms with Python-native idioms."

‎tests/unit/harness/test_software_criterion_snapshots.py‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import json
4+
import tomllib
35
from dataclasses import replace
46
from pathlib import Path
57
from typing import TYPE_CHECKING, Any
@@ -88,6 +90,55 @@ def test_py_software_criterion_control_flow_v1_snapshot(
8890
)
8991

9092

93+
def test_py_software_criterion_control_flow_v1_scenario_benchmark_contract() -> None:
94+
scenario = tomllib.loads((_SCENARIO / "scenario.toml").read_text(encoding="utf-8"))
95+
benchmark = tomllib.loads(
96+
(_SCENARIO / "benchmark.toml").read_text(encoding="utf-8")
97+
)
98+
findings = json.loads(
99+
(_SCENARIO / "expect" / "findings.json").read_text(encoding="utf-8")
100+
)
101+
102+
assert scenario["policy_ids"] == [
103+
"PYTHON-AGENT-CONTROL-FLOW-001",
104+
"PYTHON-AGENT-NATIVE-IDIOM-001",
105+
]
106+
assert scenario["inputs"] == "inputs"
107+
assert scenario["expected"] == "expect"
108+
assert _fixture_files(_SCENARIO / scenario["inputs"])
109+
assert _fixture_files(_SCENARIO / scenario["expected"])
110+
111+
trigger = scenario["policy_trigger"]
112+
expected_rule_ids = trigger["expected_rule_ids"]
113+
expected_criteria = trigger["expected_criteria"]
114+
assert trigger["kind"] == "software-criterion"
115+
assert trigger["evidence"] == "expect/findings.json"
116+
assert "inputs/criterion.py" in trigger["trigger"]
117+
assert "AI agent" in trigger["agent_failure_mode"]
118+
assert "Split" in trigger["expected_resolution"]
119+
assert expected_rule_ids == [
120+
"PY-AGENT-POLICY-009",
121+
"PY-AGENT-POLICY-010",
122+
"PY-AGENT-POLICY-011",
123+
]
124+
assert {finding["rule_id"] for finding in findings} == set(expected_rule_ids)
125+
assert {finding["softwareCriteria"] for finding in findings} == set(
126+
expected_criteria
127+
)
128+
129+
assert benchmark["harness"] == "pytest"
130+
assert (
131+
benchmark["test"] == "tests/unit/harness/test_software_criterion_snapshots.py"
132+
)
133+
_assert_benchmark_durations(benchmark)
134+
comparison = benchmark["input_expected_comparison"]
135+
assert comparison["input_total"] == "19ms"
136+
assert comparison["expected_total"] == "8ms"
137+
assert comparison["input_memory_bytes"] == 8388608
138+
assert comparison["expected_memory_bytes"] == 6291456
139+
assert "low-quality input patterns" in comparison["interpretation"]
140+
141+
91142
def _copy_inputs(source_dir: Path, destination_dir: Path) -> None:
92143
for source in source_dir.rglob("*"):
93144
if source.is_dir():
@@ -109,3 +160,24 @@ def _filter_software_criterion_findings(
109160
in {"PY-AGENT-POLICY-009", "PY-AGENT-POLICY-010", "PY-AGENT-POLICY-011"}
110161
),
111162
)
163+
164+
165+
def _fixture_files(root: Path) -> list[Path]:
166+
return [path for path in root.rglob("*") if path.is_file()]
167+
168+
169+
def _assert_benchmark_durations(benchmark: dict[str, Any]) -> None:
170+
for field in [
171+
"target_total",
172+
"max_total",
173+
"observed_total",
174+
"regression_budget",
175+
]:
176+
value = benchmark[field]
177+
assert isinstance(value, str)
178+
suffix = next(
179+
suffix for suffix in ("ns", "us", "ms", "s") if value.endswith(suffix)
180+
)
181+
assert value.removesuffix(suffix).isdigit()
182+
assert benchmark["memory_budget_bytes"] > 0
183+
assert benchmark["observed_memory_bytes"] > 0
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
schema = "schemas/semantic-scenario-benchmark.v1.schema.json"
2+
agent_policy_id_schema = "schemas/semantic-agent-policy-id.v1.schema.json"
3+
language = "python"
4+
scenario_id = "python-control-flow-v1"
5+
scenario_path = "tests/unit/harness/scenarios/software_criteria/control_flow_v1/scenario.toml"
6+
benchmark_path = "tests/unit/harness/scenarios/software_criteria/control_flow_v1/benchmark.toml"
7+
policy_ids = ["PYTHON-AGENT-CONTROL-FLOW-001", "PYTHON-AGENT-NATIVE-IDIOM-001"]
8+
inputs = "tests/unit/harness/scenarios/software_criteria/control_flow_v1/inputs"
9+
expected = "tests/unit/harness/scenarios/software_criteria/control_flow_v1/expect"
10+
harness = "pytest"
11+
test = "tests/unit/harness/test_software_criterion_snapshots.py"
12+
policy_trigger = "pytest software criterion snapshot reads inputs/criterion.py and compares expect/findings.json plus expect/rendered.txt"
13+
expected_rule_ids = ["PY-AGENT-POLICY-009", "PY-AGENT-POLICY-010", "PY-AGENT-POLICY-011"]
14+
expected_criteria = [
15+
"control-flow.decision-stack",
16+
"control-flow.literal-dispatch-chain",
17+
"control-flow.traversal-knot",
18+
"control-flow.broad-linear-phase",
19+
"native-idiom.manual-transform-loop",
20+
]
21+
input_expected_comparison = "expected fixture exposes branch shape and native transforms before agent edits; observed policy receipts catch five low-quality input patterns."
22+
snapshot_role = "scenario-benchmark-policy-trigger-contract"

‎tests/unit/snapshots/scenario_benchmark__python_json_boundary_v1.snap‎

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)