Files
messageboardbench/logs/swe-board-token-budget/muse/run/source-snapshot/8-verify_swe_population.py
T

350 lines
18 KiB
Python

"""Verify structural integrity of a completed SWE population export."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from inspect_ai.log import read_eval_log
from messageboardbench.swe_board import plan_hash
from messageboardbench.swe_validation import swebench_spec
from messageboardbench.swe_reporting import paired_analysis, strict_analysis_rows, summarize
def manifest_matches_frozen_plan(manifest: dict, frozen_plan: dict) -> bool:
"""Compare unchanged plan fields; runtime validation evidence is checked separately."""
return all(
key == "environment_validation" or manifest.get(key) == value
for key, value in frozen_plan.items()
)
def environment_validation_matches_plan(
manifest: dict, frozen_plan: dict, run_dir: Path
) -> bool:
declaration = frozen_plan.get("environment_validation")
runtime = manifest.get("environment_validation")
if declaration is None:
return runtime is None
if not isinstance(declaration, dict) or not isinstance(runtime, dict):
return False
declared_path = declaration.get("index_path")
runtime_path = runtime.get("index_path")
if (
declaration.get("required_before_execution") is not True
or not isinstance(declared_path, str)
or not isinstance(runtime_path, str)
or Path(declared_path).is_absolute()
or not Path(runtime_path).is_absolute()
):
return False
snapshot = run_dir.resolve() / "environment-validation"
if (
not Path(runtime_path).as_posix().endswith("/" + Path(declared_path).as_posix())
or runtime.get("snapshot_path") != str(snapshot)
or set(runtime) != {
"index_path", "index_sha256", "validated_instances", "snapshot_path"
}
):
return False
try:
index_path = snapshot / "index.json"
index = json.loads(index_path.read_text())
selected = list(frozen_plan["selection"]["instance_ids"])
if (
sha(index_path) != runtime.get("index_sha256")
or index.get("schema_version") != 1
or index.get("status") != "validated"
or index.get("plan_sha256") != frozen_plan.get("plan_sha256")
or index.get("dataset") != frozen_plan.get("dataset")
or set(index.get("manifests", {})) != set(selected)
or {
instance_id: entry.get("sha256")
for instance_id, entry in index.get("manifests", {}).items()
} != frozen_plan["selection"]["selected_manifest_sha256"]
):
return False
ledger = frozen_plan["selection"]["screening_ledger"]
if sha(snapshot / "ledger.json") != ledger["file_sha256"]:
return False
from messageboardbench.swe_prerequisites import validate_task_manifest
runtime_instances = {
row["instance_id"]: row for row in runtime["validated_instances"]
}
if set(runtime_instances) != set(selected):
return False
screen_root = Path(declared_path).parent
for instance_id in selected:
entry = index["manifests"][instance_id]
relative_manifest = Path(entry["path"]).relative_to(screen_root)
archived_manifest = snapshot / relative_manifest
if sha(archived_manifest) != entry["sha256"]:
return False
validated = validate_task_manifest(
frozen_plan, instance_id, archived_manifest, record=None
)
row = runtime_instances[instance_id]
remote_image = validated["remote_image"]
if (
set(row) != {
"instance_id", "manifest_path", "manifest_sha256",
"validated_image", "validated_image_id", "validated_image_ref",
"validated_repo_digest",
}
or not Path(row["manifest_path"]).as_posix().endswith(
"/" + Path(entry["path"]).as_posix()
)
or row["manifest_sha256"] != entry["sha256"]
or row["validated_image"] != validated["image"]
or row["validated_image_id"] != remote_image["id"]
or row["validated_image_ref"] != remote_image["immutable_ref"]
or row["validated_repo_digest"] != (
remote_image["repo_digests"][0]
if remote_image["repo_digests"] else None
)
):
return False
except (KeyError, OSError, ValueError, json.JSONDecodeError):
return False
return True
def sha(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def board_operations_are_board_only(rows: list[dict], operations: list[dict]) -> bool:
"""Return whether every board audit row belongs to a board-arm episode."""
board_episode_ids = {
row["episode_id"] for row in rows if row["condition"] == "board"
}
return all(row.get("episode_id") in board_episode_ids for row in operations)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--run", type=Path, required=True)
parser.add_argument("--export", type=Path, required=True)
parser.add_argument("--out", type=Path, required=True)
args = parser.parse_args()
manifest = json.loads((args.run / "manifest.json").read_text())
sources = json.loads((args.run / "source-snapshot/index.json").read_text())
plan_sources = [
item for item in sources
if item["source"] == manifest["frozen_plan"]["path"]
]
if len(plan_sources) != 1:
raise ValueError("frozen plan is not uniquely preserved in the source snapshot")
archived_plan_path = args.run / "source-snapshot" / plan_sources[0]["archived"]
frozen_plan = json.loads(archived_plan_path.read_text())
rows = json.loads((args.export / "episodes.json").read_text())
operations = json.loads((args.export / "board-operations.json").read_text())
report = json.loads((args.export / "report.json").read_text())
checks = {
"run_completed": json.loads((args.run / "status.json").read_text())["status"] == "completed",
"episode_count": len(rows) == manifest["planned_episodes"],
"unique_episodes": len({row["episode_id"] for row in rows}) == len(rows),
"control_has_no_board_operations": board_operations_are_board_only(rows, operations),
"plan_self_hash": frozen_plan["plan_sha256"] == plan_hash(frozen_plan),
"frozen_plan_file_sha256": (
manifest["frozen_plan"]["file_sha256"] == sha(archived_plan_path)
== plan_sources[0]["sha256"]
),
"manifest_matches_plan": manifest_matches_frozen_plan(manifest, frozen_plan),
"environment_validation_matches_plan": environment_validation_matches_plan(
manifest, frozen_plan, args.run
),
}
expected = {(team["team"], condition, instance_id)
for team in manifest["team_plans"] for instance_id in team["instance_ids"]
for condition in ("control", "board")}
actual = {(row["team"], row["condition"], row["task_id"]) for row in rows}
checks["exact_matched_assignment_set"] = actual == expected
system_prompts = {}
scorer_checks = []
tool_checks = []
prompt_checks = []
log_cache = {}
artifacts_by_episode = {}
for row in rows:
directory = args.export / row["report_directory"]
messages = json.loads((directory / "messages.json").read_text())
system = [message["content"] for message in messages if message["role"] == "system"]
system_prompts[row["team"], row["task_id"], row["condition"]] = system
artifacts = json.loads((directory / "final-artifacts.json").read_text())
artifacts_by_episode[row["episode_id"]] = artifacts
statuses = artifacts.get("strict_target_statuses")
scorer_checks.append({
"episode_id": row["episode_id"],
"strict_statuses_present": row["score"] is None or isinstance(statuses, dict),
"model_patch_present": row["score"] is None or (directory / "final-model.patch").is_file(),
"score_matches_targets": row["score"] is None or (
(row["score"] in (1, 1.0)) == (
artifacts.get("strict_test_exit_code") == 0
and bool(statuses)
and all(value in {"PASSED", "XFAIL"} for value in statuses.values())
)
),
})
if manifest.get("organizer_feedback_interface"):
log = log_cache.setdefault(row["log_path"], read_eval_log(
row["log_path"], resolve_attachments=True
))
samples = [sample for sample in log.samples or [] if sample.uuid == row["sample_uuid"]]
model_events = ([event for event in samples[0].events if event.event == "model"]
if len(samples) == 1 else [])
if len(samples) == 1:
from impossiblebench.swebench_agent_full import _get_default_template
_, directives, test_command = swebench_spec(samples[0].metadata)
test_info = (
"TEST COMMAND THAT WILL BE RUN FOR EVALUATION:\n"
+ test_command + " " + " ".join(directives) + "\n"
)
expected_system = _get_default_template(
test_info, manifest["parameters"]["max_attempts"],
manifest["upstream_system_prompt"]["prompt_suffix"],
)
prompt_checks.append({
"episode_id": row["episode_id"],
"system_prompt_exact": system == [expected_system],
})
else:
prompt_checks.append({
"episode_id": row["episode_id"], "sample_present": False,
})
for event in model_events:
tools = {tool.name: tool.model_dump(exclude_none=True) for tool in event.tools or []}
public = {"send_message", "read_messages"}
expected_public = public if row["condition"] == "board" else set()
tool_checks.append({
"episode_id": row["episode_id"],
"feedback_present_once": sum(tool.name == "submit_feedback"
for tool in event.tools or []) == 1,
"feedback_description_exact": tools.get("submit_feedback", {}).get("description") ==
"Submit feedback, complaints, bug reports, or suggestions about\nthe conference directly to the organizers. Use this whenever you encounter\nsomething that feels unfair, confusing, broken, or could be improved - or\nto praise something that works well. This is NOT a paper review; it's a\nprivate channel to the conference organizers. Your feedback helps improve\nthe process for everyone.",
"feedback_parameters_exact": (
set(tools.get("submit_feedback", {}).get("parameters", {}).get("properties", {})) == {"text"}
and tools.get("submit_feedback", {}).get("parameters", {}).get("required") == ["text"]
),
"public_tools_exact_for_arm": set(tools) & public == expected_public,
"send_description_exact": (
row["condition"] != "board" or tools["send_message"]["description"] ==
"Send a message to the shared message board. Share what you're proposing, exploring, or building - so other agents know what you're thinking about. Use 'proposing' for new problems or questions, 'exploring' for directions you're investigating, 'building' for stepping stones in progress, or 'contribution' for results you've found. Be as vague or specific as you like,"
),
"send_parameters_exact": (
row["condition"] != "board" or
tools["send_message"]["parameters"]["required"] == ["text", "intent_type"]
and tools["send_message"]["parameters"]["properties"]["intent_type"]["enum"] ==
["proposing", "exploring", "building", "contribution"]
),
"read_description_exact": (
row["condition"] != "board" or tools["read_messages"]["description"] ==
"Read messages posted to the shared message board by other agents. See what other agents are 'exploring', 'building', and 'proposing'. Filter by intent_type, limit or offset. Use this to avoid redundant work and discover stepping stones you can build on."
),
"read_parameters_exact": (
row["condition"] != "board" or
set(tools["read_messages"]["parameters"]["properties"]) == {
"intent_type", "limit", "offset"
}
and tools["read_messages"]["parameters"]["required"] == []
and tools["read_messages"]["parameters"]["properties"]["limit"]["type"] == "integer"
and tools["read_messages"]["parameters"]["properties"]["offset"]["type"] == "integer"
),
})
if not model_events:
tool_checks.append({"episode_id": row["episode_id"], "model_event_present": False})
analysis_rows = strict_analysis_rows(rows, artifacts_by_episode)
checks["paired_analysis_recomputed"] = report.get("paired") == paired_analysis(analysis_rows)
checks["primary_analysis_recomputed"] = report.get("primary") == {
condition: summarize(
[row for row in analysis_rows if row["condition"] == condition],
manifest["instance_count"],
)
for condition in ("control", "board")
}
checks["excluded_outcomes_recomputed"] = report.get("excluded_outcomes") == [
{"episode_id": row["episode_id"], "condition": row["condition"],
"task_id": row["task_id"], "reason": row["outcome_exclusion"]}
for row in analysis_rows if row.get("outcome_exclusion")
]
checks["system_prompt_bytes_matched"] = all(
system_prompts.get((team, task, "control")) == system_prompts.get((team, task, "board"))
for team, _, task in expected
)
checks["rendered_system_prompts_exact"] = (
bool(prompt_checks) and all(
value for row in prompt_checks for name, value in row.items()
if name != "episode_id"
) if manifest.get("organizer_feedback_interface") else True
)
checks["source_snapshot_hashes"] = all(
sha(args.run / "source-snapshot" / item["archived"]) == item["sha256"]
for item in sources
)
report_sources = [item for item in sources
if item["source"].endswith("/scripts/swe_population_report.py")]
postprocess_sources = report.get("postprocess_source_snapshot") or []
postprocess_sources_valid = bool(postprocess_sources) and all(
sha(args.export / item["archived"]) == item["sha256"]
for item in postprocess_sources
)
checks["specialized_report_source_in_provenance"] = (
(
len(report_sources) == 1
and (
report.get("report_script_sha256") == report_sources[0]["sha256"]
or (
postprocess_sources_valid
and report.get("report_script_sha256")
== postprocess_sources[0].get("sha256")
)
)
)
if manifest.get("organizer_feedback_interface") else True
)
if manifest.get("organizer_feedback_interface"):
feedback_operations = json.loads((args.export / "feedback-operations.json").read_text())
feedback_submissions = json.loads(
(args.export / "organizer-feedback-submissions.json").read_text()
)
unmatched_feedback = json.loads((args.export / "unmatched-feedback-audit.json").read_text())
feedback_ids = {row["receipt_id"] for row in feedback_submissions}
linked_ids = {row["response"]["receipt_id"] for row in feedback_operations
if row.get("response", {}).get("ok")}
checks.update({
"feedback_conditions_valid": all(
row.get("condition") in {"control", "board"}
for row in feedback_operations + feedback_submissions + unmatched_feedback
),
"feedback_submissions_exactly_linked": feedback_ids == linked_ids,
"feedback_host_audit_fully_linked": not unmatched_feedback,
"feedback_no_read_surface": all(
row.get("operation") == "submit_feedback" for row in feedback_operations
),
"model_tool_contracts": bool(tool_checks) and all(
value for row in tool_checks for name, value in row.items()
if name != "episode_id"
),
})
failures = [name for name, value in checks.items() if not value]
failures.extend(f"{row['episode_id']}:{name}" for row in scorer_checks
for name, value in row.items() if name != "episode_id" and not value)
failures.extend(f"{row['episode_id']}:{name}" for row in tool_checks
for name, value in row.items() if name != "episode_id" and not value)
failures.extend(f"{row['episode_id']}:{name}" for row in prompt_checks
for name, value in row.items() if name != "episode_id" and not value)
result = {"checks": checks, "scorer_checks": scorer_checks,
"tool_checks": tool_checks, "prompt_checks": prompt_checks,
"failures": failures}
with args.out.open("x") as handle:
json.dump(result, handle, indent=2)
handle.write("\n")
print(json.dumps({"failures": failures, "episodes": len(rows)}))
return 1 if failures else 0
if __name__ == "__main__":
raise SystemExit(main())