"""Deterministic, fail-closed Markdown reporting for benchmark evidence. This module is intentionally a reader of the immutable run tree. It does not retry execution or scoring, derive values that producers did not record, or write anything except the idempotent ``report.md`` artifact after every input has passed its owning strict loader. """ from __future__ import annotations import json import os import stat from dataclasses import dataclass from pathlib import Path, PurePosixPath from typing import Any from scripts.agent_benchmark import scoring as _scoring from scripts.agent_benchmark.attempts import ( Attempt, AttemptStateError, RunIdentity, RunStore, _connectivity_result_from_payload, ) from scripts.agent_benchmark.manifest import Manifest, MatrixCell from scripts.agent_benchmark.measurement import ( METRIC_NAMES, TIMELINE_NAMES, AttemptMeasurement, MeasurementError, Observation, load_measurement, ) from scripts.agent_benchmark.web_validation import ( WebValidation, WebValidationError, load_web_validation, ) from scripts.agent_benchmark.rubric import validate_worksheet REPORT_FILENAME = "report.md" _MAX_REPORT_BYTES = 2 * 1024 * 1024 class ReportError(Exception): """The immutable evidence cannot safely be projected into a report.""" @dataclass(frozen=True) class CategoryProjection: id: str score: int max_score: int @dataclass(frozen=True) class ScoreProjection: status: str categories: tuple[CategoryProjection, ...] total: int | None rank: int | None reasons: tuple[str, ...] score_id: str | None evaluator: tuple[str, str, str, str] | None raw_paths: tuple[str, ...] @dataclass(frozen=True) class AttemptProjection: cell: MatrixCell attempt: Attempt measurement: AttemptMeasurement | None web: WebValidation | None score: ScoreProjection raw_paths: tuple[str, ...] @dataclass(frozen=True) class ReportProjection: run: RunIdentity manifest: Manifest preflights: tuple[dict[str, Any], ...] attempts: tuple[AttemptProjection, ...] def _markdown(value: object) -> str: """Escape one table cell without allowing a value to alter Markdown shape.""" return str(value).replace("\\", "\\\\").replace("|", "\\|").replace( "\r", " ").replace("\n", " ") def _relative_path(value: str) -> str: if not isinstance(value, str) or not value or "\\" in value or ":" in value: raise ReportError("report evidence path is invalid") path = PurePosixPath(value) if path.is_absolute() or str(path) != value or any( part in ("", ".", "..") for part in path.parts ): raise ReportError("report evidence path is invalid") return value def _regular_under(root: Path, relative: str) -> None: """Require a contained regular file and reject every symlink component.""" relative = _relative_path(relative) current = root try: root_info = os.lstat(root) except OSError as exc: raise ReportError("report run root is unavailable") from exc if not stat.S_ISDIR(root_info.st_mode) or stat.S_ISLNK(root_info.st_mode): raise ReportError("report run root is invalid") for component in PurePosixPath(relative).parts: current = current / component try: info = os.lstat(current) except OSError as exc: raise ReportError("report evidence is unavailable") from exc if stat.S_ISLNK(info.st_mode): raise ReportError("report evidence path is invalid") if not stat.S_ISREG(info.st_mode): raise ReportError("report evidence must be a regular file") try: current.resolve().relative_to(root.resolve()) except ValueError as exc: raise ReportError("report evidence escapes run root") from exc def _raw_link(path: str) -> str: path = _relative_path(path) return f"[raw](<{path.replace('>', '%3E')}>)" def _attempt_label(item: AttemptProjection) -> str: """Render the complete durable identity used by detailed report rows.""" identity = item.attempt.identity return f"{item.cell.id}/r{identity.repetition}/a{identity.attempt}" def _observation(value: Observation) -> str: if value.status == "observed": return ( f"{value.value} {value.unit}; clock={value.clock}; " f"source={value.source}" ) return f"unavailable; reason={value.reason}; source={value.source}" def _observation_group(values: dict[str, Observation]) -> str: """Keep every named value while coalescing identical unavailable causes.""" groups: dict[tuple[str, str], list[str]] = {} rendered: list[str] = [] for name, value in values.items(): if value.status == "observed": rendered.append(f"{name}={_observation(value)}") else: groups.setdefault((value.reason, value.source), []).append(name) for (reason, source), names in groups.items(): rendered.append( f"{','.join(names)}=unavailable; reason={reason}; source={source}" ) return "; ".join(rendered) def _read_canonical_json(path: Path, label: str) -> dict[str, Any]: """Use scoring's no-follow reader and reject non-canonical JSON records.""" try: value = _scoring._load_canonical(path, label) except Exception as exc: raise ReportError("report scoring evidence is unavailable") from exc if not isinstance(value, dict): raise ReportError("report scoring evidence is invalid") return value def _score_projection( run: RunIdentity, manifest: Manifest, attempt: Attempt, *, blocked: bool ) -> ScoreProjection: """Strictly load the terminal score state without allocating or recovering.""" root = Path(attempt.root) raw_paths: list[str] = [] try: score_root = _scoring._score_root(attempt, create=False) score_dirs = _scoring._score_dirs(score_root) is_unscored = _scoring._validate_unscored(run, manifest, attempt) if is_unscored: if score_dirs: raise ReportError("report scoring state is invalid") raw_paths.append( f"cells/{attempt.identity.cell_id}/repetition-" f"{attempt.identity.repetition:04d}/attempt-" f"{attempt.identity.attempt:06d}/scoring/unscored.json" ) record = _read_canonical_json(root / "scoring" / "unscored.json", "unscored evidence") reasons = record.get("reasons") if not isinstance(reasons, list) or not all(isinstance(item, str) for item in reasons): raise ReportError("report unscored evidence is invalid") return ScoreProjection( "unscored", (), None, None, tuple(reasons), None, None, tuple(raw_paths), ) if not score_dirs: return ScoreProjection( "blocked" if blocked else "unavailable", (), None, None, ("evaluator_preflight_blocked",) if blocked else ("not_recorded",), None, None, (), ) statuses: list[str] = [] allocations: list[dict[str, Any]] = [] for score_root in score_dirs: status = _scoring._result_status(score_root, run, manifest, attempt) if status is None: raise ReportError("report scoring result is incomplete") statuses.append(status) allocations.append( _scoring._validate_allocation( score_root / "allocation.json", run, manifest, attempt, score_root.name ) ) prefix = ( f"cells/{attempt.identity.cell_id}/repetition-" f"{attempt.identity.repetition:04d}/attempt-" f"{attempt.identity.attempt:06d}/scoring/{score_root.name}" ) raw_paths.extend((f"{prefix}/allocation.json", f"{prefix}/result.json")) if "scored" in statuses: if statuses[-1] != "scored" or statuses.count("scored") != 1: raise ReportError("report scoring state is invalid") score_root = score_dirs[-1] result = _read_canonical_json(score_root / "result.json", "scoring result") try: worksheet = validate_worksheet(result.get("worksheet")) except Exception as exc: raise ReportError("report worksheet is invalid") from exc categories = tuple( CategoryProjection(item.id, item.score, item.max_score) for item in worksheet.categories ) if not categories: raise ReportError("report worksheet is invalid") evaluator = allocations[-1]["evaluator"] binding = ( evaluator["caller"], evaluator["route_id"], evaluator["request_model"], evaluator["requested_effort"], ) return ScoreProjection( "scored", categories, worksheet.total, None, (), score_root.name, binding, tuple(raw_paths), ) if any(status != "scoring_failed" for status in statuses): raise ReportError("report scoring state is invalid") result = _read_canonical_json(score_dirs[-1] / "result.json", "scoring result") reason = result.get("reason") if not isinstance(reason, str) or not reason: raise ReportError("report scoring failure is invalid") evaluator = allocations[-1]["evaluator"] binding = ( evaluator["caller"], evaluator["route_id"], evaluator["request_model"], evaluator["requested_effort"], ) return ScoreProjection( "scoring_failed", (), None, None, (reason,), score_dirs[-1].name, binding, tuple(raw_paths), ) except ReportError: raise except Exception as exc: raise ReportError("report scoring evidence is invalid") from exc def _scoring_preflight_blocked(run: RunIdentity, manifest: Manifest) -> bool: """Validate evaluator preflights and report whether the latest one blocked.""" root = Path(run.root) / "scoring-preflight" if not root.exists() and not root.is_symlink(): return False try: if root.is_symlink() or not root.is_dir(): raise ReportError("report scoring preflight is invalid") evaluator = _scoring._evaluator_cell(manifest) statuses: list[str] = [] for expected, path in enumerate(sorted(root.iterdir()), start=1): if path.name != f"preflight-{expected:06d}.json": raise ReportError("report scoring preflight sequence is invalid") payload = _read_canonical_json(path, "evaluator preflight") result, _, _ = _connectivity_result_from_payload(payload, evaluator) statuses.append(result.status) return bool(statuses and statuses[-1] != "ready") except ReportError: raise except Exception as exc: raise ReportError("report scoring preflight is invalid") from exc def _rank(attempts: list[AttemptProjection]) -> tuple[AttemptProjection, ...]: """Assign competition ranks without using display order as a tie-breaker.""" scored = sorted( (item for item in attempts if item.score.status == "scored"), key=lambda item: -int(item.score.total), ) ranks: dict[tuple[str, int, int], int] = {} previous: int | None = None for index, item in enumerate(scored, start=1): total = int(item.score.total) if total != previous: rank = index previous = total ranks[(item.attempt.identity.cell_id, item.attempt.identity.repetition, item.attempt.identity.attempt)] = rank result: list[AttemptProjection] = [] for item in attempts: key = (item.attempt.identity.cell_id, item.attempt.identity.repetition, item.attempt.identity.attempt) score = item.score result.append(AttemptProjection( item.cell, item.attempt, item.measurement, item.web, ScoreProjection( score.status, score.categories, score.total, ranks.get(key), score.reasons, score.score_id, score.evaluator, score.raw_paths, ), item.raw_paths, )) return tuple(result) def project_report(store: RunStore, run: RunIdentity, manifest: Manifest) -> ReportProjection: """Read every report input in stable manifest/slot/attempt order.""" try: bound = store.open(manifest, run.run_id) if bound != run: raise ReportError("report run identity is invalid") preflights = store.preflights(bound, manifest) blocked = _scoring_preflight_blocked(bound, manifest) cells = {cell.id: cell for cell in manifest.matrix} rows: list[AttemptProjection] = [] for attempt in store.execution_attempts(bound, manifest): cell = cells.get(attempt.identity.cell_id) if cell is None: raise ReportError("report attempt cell is invalid") measurement = None web = None raw_paths = [ f"cells/{attempt.identity.cell_id}/repetition-" f"{attempt.identity.repetition:04d}/attempt-" f"{attempt.identity.attempt:06d}/attempt.json", ] if attempt.state != "running": measurement = load_measurement(attempt.root) if ( measurement.run_id != bound.run_id or measurement.cell_id != attempt.identity.cell_id or measurement.repetition != attempt.identity.repetition or measurement.attempt != attempt.identity.attempt or measurement.caller != cell.caller ): raise ReportError("report measurement identity is invalid") web = load_web_validation(attempt.root, manifest=manifest) if web.record["attempt"] != { "run_id": bound.run_id, "cell_id": attempt.identity.cell_id, "repetition": attempt.identity.repetition, "attempt": attempt.identity.attempt, }: raise ReportError("report web evidence identity is invalid") raw_paths.extend(( f"cells/{attempt.identity.cell_id}/repetition-{attempt.identity.repetition:04d}/attempt-{attempt.identity.attempt:06d}/attempt-measurement.json", f"cells/{attempt.identity.cell_id}/repetition-{attempt.identity.repetition:04d}/attempt-{attempt.identity.attempt:06d}/web-validation.json", )) score = _score_projection(bound, manifest, attempt, blocked=blocked) rows.append(AttemptProjection(cell, attempt, measurement, web, score, tuple(raw_paths))) return ReportProjection(bound, manifest, preflights, _rank(rows)) except ReportError: raise except (AttemptStateError, MeasurementError, WebValidationError) as exc: raise ReportError("report evidence is invalid") from exc except Exception as exc: raise ReportError("report evidence is unavailable") from exc def render_report(projection: ReportProjection) -> bytes: """Render one fixed-order UTF-8/LF Markdown projection.""" root = Path(projection.run.root) raw_paths = {"manifest.json", "run.json"} scoring_preflight = root / "scoring-preflight" if scoring_preflight.exists() or scoring_preflight.is_symlink(): # project_report already performed the schema validation; retain the # exact immutable preflight bytes as the blocked/ready score pointer. for expected, path in enumerate(sorted(scoring_preflight.iterdir()), start=1): if path.name != f"preflight-{expected:06d}.json": raise ReportError("report scoring preflight sequence is invalid") raw_paths.add(f"scoring-preflight/{path.name}") lines = [ "# Agent comparison benchmark report", "", "## Run identity", "", "| field | value |", "|---|---|", f"| run_id | {_markdown(projection.run.run_id)} |", f"| manifest_digest | {_markdown(projection.run.manifest_digest)} |", f"| pipeline_version | {_markdown(projection.manifest.pipeline_version)} |", "", "## Immutable conditions", "", "| field | value |", "|---|---|", f"| environment | {_markdown(projection.manifest.environment)} |", f"| fixture | {_markdown(projection.manifest.fixture.version)} ({_markdown(projection.manifest.fixture.checksum)}) |", f"| rubric | {_markdown(projection.manifest.rubric_version)} |", f"| session_policy | {_markdown(projection.manifest.session_policy)} |", f"| setup_cache_policy | {_markdown(projection.manifest.setup_cache_policy)} |", f"| evaluator | {_markdown(projection.manifest.evaluator.caller)}/{_markdown(projection.manifest.evaluator.iop.request_model)}/{_markdown(projection.manifest.evaluator.iop.requested_effort)} |", "", "## Execution preflight", "", "| sequence | status | results |", "|---:|---|---:|", ] if projection.preflights: for item in projection.preflights: path = f"preflight/preflight-{item['sequence']:06d}.json" raw_paths.add(path) lines.append(f"| {item['sequence']} | {_markdown(item['status'])} | {len(item['results'])} |") else: lines.append("| — | unavailable | 0 |") lines.extend(("", "## Attempt outcomes", "", "| cell | repetition | attempt | execution | terminal | web | scoring | total | rank |", "|---|---:|---:|---|---|---|---|---:|---:|")) if not projection.attempts: lines.append("| — | — | — | blocked | unavailable | unavailable | unavailable | — | — |") for item in projection.attempts: terminal = "unavailable" if item.measurement is None else item.measurement.terminal_reason web = "unavailable" if item.web is None else item.web.status total = "—" if item.score.total is None else str(item.score.total) rank = "—" if item.score.rank is None else str(item.score.rank) lines.append( f"| {_markdown(item.cell.id)} | {item.attempt.identity.repetition} | {item.attempt.identity.attempt} | " f"{_markdown(item.attempt.state)} | {_markdown(terminal)} | {_markdown(web)} | " f"{_markdown(item.score.status)} | {total} | {rank} |" ) raw_paths.update(item.raw_paths) raw_paths.update(item.score.raw_paths) lines.extend(( "", "## Quality score breakdown", "", "| cell/repetition/attempt | category | score | max |", "|---|---|---:|---:|", )) wrote_categories = False for item in projection.attempts: label = _attempt_label(item) for category in item.score.categories: lines.append( f"| {_markdown(label)} | {_markdown(category.id)} | " f"{category.score} | {category.max_score} |" ) wrote_categories = True if not wrote_categories: lines.append("| — | unavailable | — | — |") lines.extend(("", "## Timing and token evidence", "", "| cell/repetition/attempt | time observations | token observations |", "|---|---|---|")) wrote_metrics = False for item in projection.attempts: if item.measurement is None: continue label = _attempt_label(item) timeline = _observation_group(item.measurement.timeline) usage = _observation_group(item.measurement.usage) lines.append(f"| {_markdown(label)} | {_markdown(timeline)} | {_markdown(usage)} |") wrote_metrics = True if not wrote_metrics: lines.append("| — | unavailable; reason=not_recorded | unavailable; reason=not_recorded |") lines.extend(("", "## Web validation and scoring provenance", "", "| cell/repetition/attempt | web gates | screenshots | score_id | evaluator | scoring condition |", "|---|---|---|---|---|---|")) if not projection.attempts: lines.append("| — | unavailable | unavailable | — | — | blocked |") for item in projection.attempts: gates, shots = "unavailable", "unavailable" if item.web is not None: gates = ", ".join( f"{gate['id']}={'pass' if gate['passed'] else 'fail'}" for gate in item.web.record["gates"] ) shots = ", ".join( str(view["screenshot"]["file"]) for view in item.web.record["viewports"] ) or "unavailable" evaluator = "unavailable" if item.score.evaluator is None else "/".join(item.score.evaluator) condition = ", ".join(item.score.reasons) or "recorded" lines.append( f"| {_markdown(_attempt_label(item))} | {_markdown(gates)} | {_markdown(shots)} | " f"{_markdown(item.score.score_id or '—')} | {_markdown(evaluator)} | {_markdown(condition)} |" ) lines.extend(("", "## Limitations", "", "- Values marked `unavailable` retain the producing source and reason; they are not inferred as zero.", "- Automatic web gates establish eligibility only and contribute no quality points.", "- Equal scored totals share a competition rank; unscored and scoring-failed attempts do not receive a rank.", "", "## Raw evidence index", "", "| contained pointer |", "|---|")) for path in sorted(raw_paths): _regular_under(root, path) lines.append(f"| {_raw_link(path)} |") lines.append("") return "\n".join(lines).encode("utf-8") def publish_report(store: RunStore, run: RunIdentity, manifest: Manifest) -> Path: """Render and idempotently publish the sole run-owned report artifact.""" data = render_report(project_report(store, run, manifest)) if len(data) > _MAX_REPORT_BYTES: raise ReportError("report exceeds the bounded artifact size") root = Path(run.root) path = root / REPORT_FILENAME try: info = os.lstat(path) except FileNotFoundError: info = None except OSError as exc: raise ReportError("report publication is unavailable") from exc if info is not None: if stat.S_ISLNK(info.st_mode) or not stat.S_ISREG(info.st_mode): raise ReportError("report publication target is invalid") try: existing = path.read_bytes() except OSError as exc: raise ReportError("report publication is unavailable") from exc if existing != data: raise ReportError("report publication refused an existing target") return path flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_CLOEXEC if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW try: descriptor = os.open(path, flags, 0o600) with os.fdopen(descriptor, "wb") as handle: handle.write(data) handle.flush() os.fsync(handle.fileno()) directory = os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC) try: os.fsync(directory) finally: os.close(directory) except OSError as exc: raise ReportError("report publication failed") from exc return path