Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions loopx/capabilities/benchmark_toolkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
select_exact_docker_container,
)
from .experiment_board import (
BenchmarkExperimentBoardValidationError,
BENCHMARK_EXPERIMENT_BOARD_LEDGER_FILENAME,
BENCHMARK_EXPERIMENT_BOARD_ROW_SCHEMA_VERSION,
BENCHMARK_EXPERIMENT_BOARD_SCHEMA_VERSION,
Expand Down Expand Up @@ -252,6 +253,7 @@
)

__all__ = [
"BenchmarkExperimentBoardValidationError",
"ATIF_SCHEMA_VERSION",
"BENCHMARK_ADAPTIVE_CONCURRENCY_DECISION_SCHEMA_VERSION",
"BENCHMARK_ADAPTIVE_CONCURRENCY_POLICY_SCHEMA_VERSION",
Expand Down
30 changes: 24 additions & 6 deletions loopx/capabilities/benchmark_toolkit/experiment_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
}


class BenchmarkExperimentBoardValidationError(ValueError):
"""A board rejection whose diagnostic is safe for caller-facing output.

Messages must contain only fixed contract text or normalized status values,
never paths, raw field names, or values from unvalidated input.
"""


def benchmark_run_status_is_terminal(value: object) -> bool:
"""Return whether a normalized run status closes its lifecycle."""

Expand Down Expand Up @@ -310,17 +318,27 @@ def normalize_benchmark_experiment_board_row(
countability = _countability(payload.get("countability"))
if countability["official_result_present"]:
if primary_metric not in metrics:
raise ValueError("official result must include primary_metric")
raise BenchmarkExperimentBoardValidationError(
"official result must include primary_metric"
)
missing_guardrails = [name for name in guardrails if name not in metrics]
if missing_guardrails:
raise ValueError("official result is missing declared guardrail metrics")
raise BenchmarkExperimentBoardValidationError(
"official result is missing declared guardrail metrics"
)
if countability["score_countable"]:
if status != "completed":
raise ValueError("score_countable requires completed status")
raise BenchmarkExperimentBoardValidationError(
"score_countable requires completed status"
)
if not countability["integrity_qualified"]:
raise ValueError("score_countable requires integrity_qualified")
raise BenchmarkExperimentBoardValidationError(
"score_countable requires integrity_qualified"
)
if not countability["official_result_present"]:
raise ValueError("score_countable requires official_result_present")
raise BenchmarkExperimentBoardValidationError(
"score_countable requires official_result_present"
)

row: dict[str, Any] = {
"schema_version": BENCHMARK_EXPERIMENT_BOARD_ROW_SCHEMA_VERSION,
Expand Down Expand Up @@ -437,7 +455,7 @@ def _validate_benchmark_run_status_transition(
existing_status = str(existing.get("status") or "")
candidate_status = str(candidate.get("status") or "")
if candidate_status not in _RUN_STATUS_TRANSITIONS.get(existing_status, set()):
raise ValueError(
raise BenchmarkExperimentBoardValidationError(
f"benchmark run status cannot move from {existing_status} to {candidate_status}"
)

Expand Down
3 changes: 3 additions & 0 deletions loopx/cli_commands/benchmark_experiment_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from ..capabilities.benchmark_toolkit import (
BENCHMARK_EXPERIMENT_BOARD_ROW_SCHEMA_VERSION,
BenchmarkExperimentBoardValidationError,
benchmark_experiment_board_row_key,
build_benchmark_experiment_board,
default_benchmark_experiment_board_path,
Expand Down Expand Up @@ -274,6 +275,8 @@ def handle_benchmark_experiment_board_command(
payload["path_recorded"] = False
if write is not None:
payload["write"] = write
except BenchmarkExperimentBoardValidationError as exc:
args.benchmark_experiment_board_parser.error(str(exc))
except (OSError, UnicodeError, json.JSONDecodeError, TypeError, ValueError):
args.benchmark_experiment_board_parser.error(
"invalid benchmark experiment board input"
Expand Down
50 changes: 49 additions & 1 deletion tests/capabilities/test_benchmark_experiment_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -964,10 +964,58 @@ def test_cli_compact_upsert_rejects_invalid_transition_without_writing(tmp_path:
"--project", str(tmp_path), "--row-json", str(row_path), "--execute", "--format", "json",
], check=False)
assert result.returncode != 0
assert "invalid benchmark experiment board input" in result.stderr
assert "benchmark run status cannot move from completed to running" in result.stderr
assert ledger.read_bytes() == before


@pytest.mark.parametrize("execute", [False, True])
def test_cli_identifies_missing_guardrails_and_allows_correction(
tmp_path: Path, execute: bool,
) -> None:
ledger = default_benchmark_experiment_board_path(project=tmp_path, goal_id="fixture-goal")
row_path = tmp_path / "row.json"
row = _baseline()
del row["metrics"]["reward"]
row_path.write_text(json.dumps(row), encoding="utf-8")
command = ["benchmark", "experiment-board-upsert", "--goal-id", "fixture-goal",
"--project", str(tmp_path), "--row-json", str(row_path), "--format", "json"]
if execute:
command.append("--execute")

rejected = run_source_cli(command, check=False)
assert rejected.returncode == 2
assert "official result is missing declared guardrail metrics" in rejected.stderr
assert not ledger.exists()

row_path.write_text(json.dumps(_baseline()), encoding="utf-8")
corrected = json.loads(run_source_cli(command).stdout)
assert corrected["row"]["metrics"]["reward"]["value"] == 0
assert corrected["write"]["write_performed"] is execute
assert ledger.exists() is execute


@pytest.mark.parametrize("failure", ["missing_file", "invalid_json", "unknown_field", "non_object"])
def test_cli_keeps_untrusted_input_errors_generic(tmp_path: Path, failure: str) -> None:
private_text = "private-input-marker"
row_path = tmp_path / (private_text + ".json")
if failure == "invalid_json":
row_path.write_text('{"' + private_text + '":', encoding="utf-8")
elif failure == "unknown_field":
row_path.write_text(json.dumps(_baseline(**{private_text: "secret"})), encoding="utf-8")
elif failure == "non_object":
row_path.write_text(json.dumps([private_text]), encoding="utf-8")
ledger = default_benchmark_experiment_board_path(project=tmp_path, goal_id="fixture-goal")
result = run_source_cli([
"benchmark", "experiment-board-upsert", "--goal-id", "fixture-goal",
"--project", str(tmp_path), "--row-json", str(row_path), "--execute", "--format", "json",
], check=False)
assert result.returncode == 2
assert "invalid benchmark experiment board input" in result.stderr
assert private_text not in result.stderr
assert str(tmp_path) not in result.stderr
assert not ledger.exists()


def test_cli_show_projects_factorial_contrast_from_explicit_contract(
tmp_path: Path,
) -> None:
Expand Down
Loading