| 1 | #!/usr/bin/env python3
|
| 2 | """Pre-publish gate: mechanical checks before any public table ships.
|
| 3 |
|
| 4 | Run against the result files you intend to publish:
|
| 5 |
|
| 6 | python3 scripts/publish_gate.py results/latest.jsonl results/latest.mount.jsonl ...
|
| 7 |
|
| 8 | Checks (fail = exit 1; manual items print as MANUAL and never fail the gate):
|
| 9 |
|
| 10 | 1. calibration docs/token-calibration-factors.md exists and is committed —
|
| 11 | published token deltas must cite calibrated factors, not
|
| 12 | raw char/4 (README warns char/4 is biased for VCS output).
|
| 13 | 2. tuned-git core-lane results include git_untracked_cache AND
|
| 14 | git_fsmonitor subject rows. Stock git alone overstates oak
|
| 15 | wins on wide trees.
|
| 16 | 3. transports no row set mixes remote transports under one operation
|
| 17 | name (remote.net.* vs remote.*); rows touching a remote
|
| 18 | carry remote_transport.
|
| 19 | 4. levels agent rows carry instruction_level and the file holds only
|
| 20 | one level (never aggregate across levels).
|
| 21 | 5. nulls null means unmeasured: summaries must not render null as 0.
|
| 22 | (Spot-checked by scanning for '| 0 |' columns whose JSONL
|
| 23 | value is null — heuristic, reported as MANUAL.)
|
| 24 | 6. skips every returncode-77 row carries skip_reason (work items,
|
| 25 | never silent absence); count is printed for the summary.
|
| 26 | 7. tails rows with tail percentiles carry their sample counts
|
| 27 | (tail_latency_summary embeds n; absence is a failure).
|
| 28 | 8. provenance published Oak subject rows carry clean Oak source
|
| 29 | provenance (hash present, status clean).
|
| 30 | 9. samples public core rows carry enough repetitions per
|
| 31 | subject/scenario/operation group (n>=30).
|
| 32 | 10. randomization public core rows state whether subject order was randomized;
|
| 33 | randomized rows carry the per-run subject order.
|
| 34 | 11. failures public rows do not hide failed operations behind successful
|
| 35 | averages.
|
| 36 | 12. tracks each result file holds one benchmark_track.
|
| 37 | 13. env-isolation public core rows carry one env isolation version so rows
|
| 38 | measured before/after harness environment policy changes
|
| 39 | cannot be compared silently.
|
| 40 | 14. conformance inputs holding Oak rows also hold diagnostic conformance rows
|
| 41 | (scripts/vcs_conformance.py --output) for every published Oak
|
| 42 | binary; the Oak rows pass the conformance gate against
|
| 43 | config/conformance_expected_failures.json (no regression, no
|
| 44 | missing behaviour, no FIXED-but-still-listed entry).
|
| 45 | """
|
| 46 |
|
| 47 | from __future__ import annotations
|
| 48 |
|
| 49 | import json
|
| 50 | import sys
|
| 51 | from pathlib import Path
|
| 52 |
|
| 53 | from oakbench.integrity import content_integrity_public_trust_passed
|
| 54 | from oakbench.public_output import output_evidence_failures, workflow_evidence_failures, workflow_qualification_failures
|
| 55 | from oakbench.rows import row_returncode
|
| 56 |
|
| 57 | ROOT = Path(__file__).resolve().parents[1]
|
| 58 | CALIBRATION_DOC = ROOT / "docs" / "token-calibration-factors.md"
|
| 59 | # Tuned subjects whose mode.setup.* rows must all succeed before any of their
|
| 60 | # rows are publishable (a failed setup means stock git measured under a tuned
|
| 61 | # label).
|
| 62 | TUNED_GIT_SUBJECTS = {"git_untracked_cache", "git_fsmonitor", "git_lfs"}
|
| 63 | # Tuned subjects every core publish must include. git_lfs is deliberately not
|
| 64 | # required here: it only applies to the binary-fixture scenarios listed in
|
| 65 | # config/git_modes.json and needs git-lfs installed, so its absence is an
|
| 66 | # honest coverage gap (skip rows), while a present-but-failed lfs setup still
|
| 67 | # fails the gate via TUNED_GIT_SUBJECTS above.
|
| 68 | REQUIRED_TUNED_GIT_SUBJECTS = {"git_untracked_cache", "git_fsmonitor"}
|
| 69 | CORE_PROFILES = {"smoke", "standard", "large", "micro"}
|
| 70 | PUBLIC_CORE_PROFILES = {"standard", "large", "micro"}
|
| 71 | PUBLIC_CORE_MIN_SAMPLES = 30
|
| 72 | EXCLUDED_SAMPLE_OP_SUFFIXES = (".add", ".commit", ".determinism", ".inforecall")
|
| 73 | EXCLUDED_SAMPLE_OP_PREFIXES = ("mode.setup",)
|
| 74 | RANDOMIZATION_FIELDS = ("randomized_subject_order", "randomize_subject_order", "subject_order_randomized")
|
| 75 | SUBJECT_ORDER_FIELDS = ("subject_order", "run_subject_order")
|
| 76 |
|
| 77 |
|
| 78 | def load_rows(path: Path) -> list[dict]:
|
| 79 | rows = []
|
| 80 | for line in path.read_text().splitlines():
|
| 81 | line = line.strip()
|
| 82 | if line:
|
| 83 | rows.append(json.loads(line))
|
| 84 | return rows
|
| 85 |
|
| 86 |
|
| 87 | def is_oak_subject(row: dict) -> bool:
|
| 88 | subject = str(row.get("subject", ""))
|
| 89 | if subject.startswith("oak"):
|
| 90 | return True
|
| 91 | if row.get("subject_kind") == "oak":
|
| 92 | return True
|
| 93 | details = row.get("subject_details")
|
| 94 | if isinstance(details, dict):
|
| 95 | subject_detail = details.get(subject)
|
| 96 | if isinstance(subject_detail, dict):
|
| 97 | return subject_detail.get("kind") == "oak" or str(
|
| 98 | subject_detail.get("role", "")
|
| 99 | ).startswith("oak_")
|
| 100 | return False
|
| 101 |
|
| 102 |
|
| 103 | def source_status_is_clean(status: object) -> bool:
|
| 104 | if not isinstance(status, str):
|
| 105 | return False
|
| 106 | normalized = " ".join(status.strip().lower().split())
|
| 107 | return normalized in {
|
| 108 | "",
|
| 109 | "clean",
|
| 110 | "no changes",
|
| 111 | "nothing to commit",
|
| 112 | "nothing to commit, working tree clean",
|
| 113 | }
|
| 114 |
|
| 115 |
|
| 116 | def randomization_value(row: dict) -> object:
|
| 117 | for field in RANDOMIZATION_FIELDS:
|
| 118 | if field in row:
|
| 119 | return row[field]
|
| 120 | return None
|
| 121 |
|
| 122 |
|
| 123 | def subject_order(row: dict) -> object:
|
| 124 | for field in SUBJECT_ORDER_FIELDS:
|
| 125 | if field in row:
|
| 126 | return row[field]
|
| 127 | return None
|
| 128 |
|
| 129 |
|
| 130 | def is_skip(row: dict) -> bool:
|
| 131 | return row_returncode(row) == 77 or bool(row.get("skipped"))
|
| 132 |
|
| 133 |
|
| 134 | def is_excluded_sample_operation(operation: str) -> bool:
|
| 135 | return operation.endswith(EXCLUDED_SAMPLE_OP_SUFFIXES) or operation.startswith(
|
| 136 | EXCLUDED_SAMPLE_OP_PREFIXES
|
| 137 | )
|
| 138 |
|
| 139 |
|
| 140 | def sample_run_id(row: dict, fallback: int) -> str:
|
| 141 | for field in ("run", "run_index"):
|
| 142 | value = row.get(field)
|
| 143 | if value is not None:
|
| 144 | return str(value)
|
| 145 | return f"row:{fallback}"
|
| 146 |
|
| 147 |
|
| 148 | def oak_binary_identity(row: dict) -> str | None:
|
| 149 | """SHA-256 of the Oak binary that produced a row, from any lane's identity field."""
|
| 150 | for key in ("binary_sha256", "subject_binary_sha256", "workflow_subject_binary_sha256"):
|
| 151 | value = row.get(key)
|
| 152 | if isinstance(value, str) and len(value) == 64:
|
| 153 | return value
|
| 154 | details = row.get("subject_details")
|
| 155 | if isinstance(details, dict):
|
| 156 | item = details.get(str(row.get("subject")))
|
| 157 | if isinstance(item, dict) and isinstance(item.get("binary_sha256"), str) and len(item["binary_sha256"]) == 64:
|
| 158 | return item["binary_sha256"]
|
| 159 | return None
|
| 160 |
|
| 161 |
|
| 162 | def conformance_gate_lines(all_rows: list[tuple[Path, dict]]) -> tuple[list[str], list[str]]:
|
| 163 | """Check 14: every published Oak binary passes the conformance gate on ITS OWN rows."""
|
| 164 | from oakbench import conformance_gate
|
| 165 | from oakbench.conformance import SCENARIOS
|
| 166 | from oakbench.conformance_matrix import BEHAVIOURS
|
| 167 | conformance_rows = [row for _, row in all_rows if row.get("evidence_class") == "diagnostic_conformance"]
|
| 168 | oak_rows = [(path, row) for path, row in all_rows
|
| 169 | if is_oak_subject(row) and row.get("evidence_class") != "diagnostic_conformance"]
|
| 170 | if not oak_rows:
|
| 171 | return [], ["PASS conformance: no Oak rows among inputs"]
|
| 172 | anonymous = [(str(path), row.get("subject"), row.get("operation")) for path, row in oak_rows
|
| 173 | if oak_binary_identity(row) is None]
|
| 174 | failures = []
|
| 175 | if anonymous:
|
| 176 | failures.append("FAIL conformance: Oak rows lack binary identity (binary_sha256 or subject_details), so "
|
| 177 | f"their conformance evidence cannot be matched: {anonymous[:5]}")
|
| 178 | published = sorted({oak_binary_identity(row) for _, row in oak_rows} - {None})
|
| 179 | if not conformance_rows:
|
| 180 | failures.append("FAIL conformance: Oak rows published without diagnostic conformance rows; run "
|
| 181 | "scripts/vcs_conformance.py --output <file> with the published Oak binary and pass it here")
|
| 182 | return failures, []
|
| 183 | try:
|
| 184 | expected = conformance_gate.load_expected()
|
| 185 | except (OSError, ValueError) as exc:
|
| 186 | return failures + [f"FAIL conformance: expected-failures list unreadable: {exc}"], []
|
| 187 | required = list(SCENARIOS) + [behaviour.id for behaviour in BEHAVIOURS]
|
| 188 | notes = []
|
| 189 | for sha in published:
|
| 190 | # Only this binary's subject rows (plus reference rows) are evaluated:
|
| 191 | # rows of another Oak build can neither satisfy nor fail this one.
|
| 192 | rows = [row for row in conformance_rows
|
| 193 | if row.get("subject") != expected["subject"] or row.get("binary_sha256") == sha]
|
| 194 | result = conformance_gate.evaluate(rows, expected, required)
|
| 195 | if not result.get("profile_recorded"):
|
| 196 | failures.append(f"FAIL conformance[{sha[:12]}]: filesystem profile {result['profiles'] or 'missing'} "
|
| 197 | "has no recorded coverage in config/conformance_expected_failures.json; publication "
|
| 198 | "requires a reviewed coverage record for the host profile")
|
| 199 | if result["verdict"] != "PASS":
|
| 200 | failures.append(f"FAIL conformance[{sha[:12]}]: {result['verdict']}: "
|
| 201 | f"{(result['regressions'] + result['unmeasured'])[:5]}")
|
| 202 | if result["fixed"]:
|
| 203 | failures.append(f"FAIL conformance[{sha[:12]}]: expected-failures list is stale (now passing): "
|
| 204 | f"{result['fixed'][:5]}")
|
| 205 | if result["verdict"] == "PASS" and not result["fixed"] and result.get("profile_recorded"):
|
| 206 | notes.append(f"PASS [{sha[:12]}] " + conformance_gate.summary_lines(result)[0])
|
| 207 | return failures, ([] if failures else notes)
|
| 208 |
|
| 209 |
|
| 210 | def main(paths: list[str]) -> int:
|
| 211 | failures: list[str] = []
|
| 212 | notes: list[str] = []
|
| 213 |
|
| 214 | if CALIBRATION_DOC.exists():
|
| 215 | notes.append(f"PASS calibration: {CALIBRATION_DOC.name} present")
|
| 216 | else:
|
| 217 | failures.append(
|
| 218 | "FAIL calibration: docs/token-calibration-factors.md missing — run "
|
| 219 | "token_calibration.py per output style before publishing token deltas"
|
| 220 | )
|
| 221 |
|
| 222 | all_rows: list[tuple[Path, dict]] = []
|
| 223 | for raw in paths:
|
| 224 | path = Path(raw)
|
| 225 | if not path.exists():
|
| 226 | failures.append(f"FAIL missing result file: {path}")
|
| 227 | continue
|
| 228 | for row in load_rows(path):
|
| 229 | all_rows.append((path, row))
|
| 230 |
|
| 231 | core_rows = [row for _, row in all_rows if row.get("profile") in CORE_PROFILES]
|
| 232 | if core_rows:
|
| 233 | subjects = {
|
| 234 | str(row.get("subject"))
|
| 235 | for row in core_rows
|
| 236 | if row_returncode(row) == 0
|
| 237 | and not is_excluded_sample_operation(str(row.get("operation") or ""))
|
| 238 | }
|
| 239 | missing = REQUIRED_TUNED_GIT_SUBJECTS - subjects
|
| 240 | if missing:
|
| 241 | failures.append(
|
| 242 | f"FAIL tuned-git: core rows lack {sorted(missing)} — rerun with "
|
| 243 | "--git-modes untracked_cache,fsmonitor (verify fsmonitor--daemon health on darwin)"
|
| 244 | )
|
| 245 | else:
|
| 246 | notes.append("PASS tuned-git: untracked_cache and fsmonitor rows present")
|
| 247 |
|
| 248 | bad_tuned_setup = [
|
| 249 | (row.get("subject"), row.get("operation"), row.get("returncode"))
|
| 250 | for row in core_rows
|
| 251 | if str(row.get("subject")) in TUNED_GIT_SUBJECTS
|
| 252 | and str(row.get("operation") or "").startswith(EXCLUDED_SAMPLE_OP_PREFIXES)
|
| 253 | and row_returncode(row) != 0
|
| 254 | ]
|
| 255 | if bad_tuned_setup:
|
| 256 | failures.append(
|
| 257 | "FAIL tuned-git: tuned Git setup failed, so tuned rows are not publishable: "
|
| 258 | f"{bad_tuned_setup[:5]}"
|
| 259 | )
|
| 260 | else:
|
| 261 | notes.append("MANUAL tuned-git: no core-lane rows among inputs; check the core table separately")
|
| 262 |
|
| 263 | oak_provenance_failures = []
|
| 264 | for path, row in all_rows:
|
| 265 | if not is_oak_subject(row) or row.get("evidence_class") == "diagnostic_conformance":
|
| 266 | continue # conformance rows carry binary identity, checked by the conformance gate
|
| 267 | source = row.get("source")
|
| 268 | source = source if isinstance(source, dict) else {}
|
| 269 | oak_hash = source.get("oak_hash")
|
| 270 | oak_status = source.get("oak_status")
|
| 271 | if not oak_hash or not source_status_is_clean(oak_status):
|
| 272 | oak_provenance_failures.append(
|
| 273 | (str(path), row.get("subject"), row.get("operation"), oak_hash, oak_status)
|
| 274 | )
|
| 275 | if oak_provenance_failures:
|
| 276 | failures.append(
|
| 277 | "FAIL provenance: published Oak rows lack clean Oak source provenance: "
|
| 278 | f"{oak_provenance_failures[:5]}"
|
| 279 | )
|
| 280 | elif any(is_oak_subject(row) for _, row in all_rows):
|
| 281 | notes.append("PASS provenance: Oak subject rows carry clean source hash/status")
|
| 282 |
|
| 283 | fake_provider_rows = [
|
| 284 | (str(path), row.get("platform"), row.get("driver"), row.get("scenario"), row.get("operation"))
|
| 285 | for path, row in all_rows
|
| 286 | if row.get("driver") == "fake-provider" or row.get("branch_triage_provider") == "fake"
|
| 287 | ]
|
| 288 | if fake_provider_rows:
|
| 289 | failures.append(
|
| 290 | "FAIL fake-provider: simulated provider rows are not publishable: "
|
| 291 | f"{fake_provider_rows[:5]}"
|
| 292 | )
|
| 293 |
|
| 294 | public_core_rows = [
|
| 295 | (path, row)
|
| 296 | for path, row in all_rows
|
| 297 | if row.get("profile") in PUBLIC_CORE_PROFILES and not is_skip(row)
|
| 298 | ]
|
| 299 | public_failures = [
|
| 300 | (str(path), row.get("subject"), row.get("scenario"), row.get("operation"), row.get("returncode"))
|
| 301 | for path, row in all_rows
|
| 302 | if row.get("profile") in PUBLIC_CORE_PROFILES
|
| 303 | and not is_skip(row)
|
| 304 | and row_returncode(row) != 0
|
| 305 | ]
|
| 306 | if public_failures:
|
| 307 | failures.append(
|
| 308 | "FAIL failures: public core rows contain failed operations: "
|
| 309 | f"{public_failures[:5]}"
|
| 310 | )
|
| 311 |
|
| 312 | workflow_evidence = workflow_evidence_failures([row for _, row in all_rows])
|
| 313 | workflow_evidence.extend(workflow_qualification_failures([row for _, row in all_rows], PUBLIC_CORE_MIN_SAMPLES))
|
| 314 | if workflow_evidence:
|
| 315 | failures.append("FAIL workflow-evidence: " + "; ".join(workflow_evidence[:5]))
|
| 316 |
|
| 317 | unsupported_output = [
|
| 318 | (str(path), row.get("subject"), row.get("scenario"), row.get("operation"), reasons)
|
| 319 | for path, row in public_core_rows
|
| 320 | for reasons in [output_evidence_failures(row)]
|
| 321 | if reasons
|
| 322 | ]
|
| 323 | if unsupported_output:
|
| 324 | failures.append(
|
| 325 | "FAIL output-evidence: public output usefulness claims lack exact evidence: "
|
| 326 | f"{unsupported_output[:5]}"
|
| 327 | )
|
| 328 |
|
| 329 | weak_content_trust = [
|
| 330 | (
|
| 331 | str(path),
|
| 332 | row.get("subject"),
|
| 333 | row.get("scenario"),
|
| 334 | row.get("operation"),
|
| 335 | row.get("content_integrity_source"),
|
| 336 | row.get("content_integrity_sources"),
|
| 337 | )
|
| 338 | for path, row in public_core_rows
|
| 339 | if row.get("content_integrity_check_passed") is True
|
| 340 | and not content_integrity_public_trust_passed(row)
|
| 341 | ]
|
| 342 | if weak_content_trust:
|
| 343 | failures.append(
|
| 344 | "FAIL content-trust: public core rows claim passing content integrity "
|
| 345 | "from weak or mixed sources: "
|
| 346 | f"{weak_content_trust[:5]}"
|
| 347 | )
|
| 348 | elif any(row.get("content_integrity_check_passed") is True for _, row in public_core_rows):
|
| 349 | notes.append("PASS content-trust: passing public content checks use strong sources")
|
| 350 |
|
| 351 | env_versions = {
|
| 352 | str(row.get("env_isolation_version"))
|
| 353 | for _, row in public_core_rows
|
| 354 | if isinstance(row.get("env_isolation_version"), str)
|
| 355 | and str(row.get("env_isolation_version")).strip()
|
| 356 | }
|
| 357 | missing_env_version = [
|
| 358 | (
|
| 359 | str(path),
|
| 360 | row.get("subject"),
|
| 361 | row.get("scenario"),
|
| 362 | row.get("operation"),
|
| 363 | row.get("run", row.get("run_index")),
|
| 364 | )
|
| 365 | for path, row in public_core_rows
|
| 366 | if not isinstance(row.get("env_isolation_version"), str)
|
| 367 | or not str(row.get("env_isolation_version")).strip()
|
| 368 | ]
|
| 369 | if missing_env_version:
|
| 370 | failures.append(
|
| 371 | "FAIL env-isolation: public core rows missing env_isolation_version: "
|
| 372 | f"{missing_env_version[:5]}"
|
| 373 | )
|
| 374 | elif len(env_versions) > 1:
|
| 375 | failures.append(
|
| 376 | "FAIL env-isolation: public core rows mix env_isolation_version values: "
|
| 377 | f"{sorted(env_versions)}"
|
| 378 | )
|
| 379 | elif env_versions:
|
| 380 | notes.append(f"PASS env-isolation: public core rows use version {next(iter(env_versions))}")
|
| 381 |
|
| 382 | measured_groups: dict[tuple[str, str, str, str, str, str], set[str]] = {}
|
| 383 | sample_groups: dict[tuple[str, str, str, str, str, str], set[str]] = {}
|
| 384 | for index, (path, row) in enumerate(public_core_rows):
|
| 385 | operation = str(row.get("operation") or "")
|
| 386 | if is_excluded_sample_operation(operation):
|
| 387 | continue
|
| 388 | key = (
|
| 389 | str(path),
|
| 390 | str(row.get("profile")),
|
| 391 | str(row.get("benchmark_track", "")),
|
| 392 | str(row.get("scenario", "")),
|
| 393 | operation,
|
| 394 | str(row.get("subject", "")),
|
| 395 | )
|
| 396 | run_id = sample_run_id(row, index)
|
| 397 | measured_groups.setdefault(key, set()).add(run_id)
|
| 398 | if row_returncode(row) == 0:
|
| 399 | sample_groups.setdefault(key, set()).add(run_id)
|
| 400 | thin_groups = {
|
| 401 | "/".join(key[1:]): len(runs)
|
| 402 | for key, runs in measured_groups.items()
|
| 403 | if len(runs) < PUBLIC_CORE_MIN_SAMPLES
|
| 404 | }
|
| 405 | zero_success_groups = {
|
| 406 | "/".join(key[1:]): len(runs)
|
| 407 | for key, runs in measured_groups.items()
|
| 408 | if not sample_groups.get(key)
|
| 409 | }
|
| 410 | if thin_groups:
|
| 411 | failures.append(
|
| 412 | f"FAIL samples: public core groups below n={PUBLIC_CORE_MIN_SAMPLES}: "
|
| 413 | f"{dict(list(thin_groups.items())[:5])}"
|
| 414 | )
|
| 415 | if zero_success_groups:
|
| 416 | failures.append(
|
| 417 | "FAIL samples: public core groups with zero successful rows: "
|
| 418 | f"{dict(list(zero_success_groups.items())[:5])}"
|
| 419 | )
|
| 420 | if public_core_rows and not thin_groups and not zero_success_groups:
|
| 421 | notes.append(f"PASS samples: public core groups have n>={PUBLIC_CORE_MIN_SAMPLES}")
|
| 422 |
|
| 423 | missing_randomization = [
|
| 424 | (
|
| 425 | str(path),
|
| 426 | row.get("subject"),
|
| 427 | row.get("scenario"),
|
| 428 | row.get("operation"),
|
| 429 | row.get("run", row.get("run_index")),
|
| 430 | )
|
| 431 | for path, row in public_core_rows
|
| 432 | if randomization_value(row) is None
|
| 433 | ]
|
| 434 | if missing_randomization:
|
| 435 | failures.append(
|
| 436 | "FAIL randomization: public core rows missing randomization metadata: "
|
| 437 | f"{missing_randomization[:5]}"
|
| 438 | )
|
| 439 | else:
|
| 440 | not_randomized = [
|
| 441 | (
|
| 442 | str(path),
|
| 443 | row.get("subject"),
|
| 444 | row.get("scenario"),
|
| 445 | row.get("operation"),
|
| 446 | row.get("run", row.get("run_index")),
|
| 447 | )
|
| 448 | for path, row in public_core_rows
|
| 449 | if randomization_value(row) is not True
|
| 450 | ]
|
| 451 | randomized_without_order = []
|
| 452 | if not_randomized:
|
| 453 | failures.append(
|
| 454 | "FAIL randomization: public core rows were not randomized: "
|
| 455 | f"{not_randomized[:5]}"
|
| 456 | )
|
| 457 | else:
|
| 458 | for path, row in public_core_rows:
|
| 459 | order = subject_order(row)
|
| 460 | if not isinstance(order, list) or not order or row.get("subject") not in order:
|
| 461 | randomized_without_order.append(
|
| 462 | (
|
| 463 | str(path),
|
| 464 | row.get("subject"),
|
| 465 | row.get("scenario"),
|
| 466 | row.get("operation"),
|
| 467 | row.get("run", row.get("run_index")),
|
| 468 | )
|
| 469 | )
|
| 470 | if randomized_without_order:
|
| 471 | failures.append(
|
| 472 | "FAIL randomization: randomized public core rows lack per-run subject order: "
|
| 473 | f"{randomized_without_order[:5]}"
|
| 474 | )
|
| 475 | elif public_core_rows:
|
| 476 | notes.append("PASS randomization: public core rows state run-order metadata")
|
| 477 |
|
| 478 | tracks_by_file: dict[Path, set[str]] = {}
|
| 479 | for path, row in all_rows:
|
| 480 | track = row.get("benchmark_track")
|
| 481 | if track:
|
| 482 | tracks_by_file.setdefault(path, set()).add(str(track))
|
| 483 | mixed_tracks = {str(path): sorted(tracks) for path, tracks in tracks_by_file.items() if len(tracks) > 1}
|
| 484 | if mixed_tracks:
|
| 485 | failures.append(f"FAIL tracks: files mix benchmark_track values: {mixed_tracks}")
|
| 486 | elif tracks_by_file:
|
| 487 | notes.append("PASS tracks: each result file holds a single benchmark_track")
|
| 488 |
|
| 489 | # Transport hygiene: remote-touching rows must state their transport, and
|
| 490 | # one subject's operation must not mix transports. Side-by-side tables may
|
| 491 | # contain the same operation name for network and local_file subjects.
|
| 492 | missing_transport = [
|
| 493 | (str(path), row.get("subject"), row.get("operation"))
|
| 494 | for path, row in all_rows
|
| 495 | if (row.get("remote_repo") or row.get("remote_server"))
|
| 496 | and not (row.get("remote_transport") or row.get("workspace_transport"))
|
| 497 | ]
|
| 498 | if missing_transport:
|
| 499 | failures.append(f"FAIL transports: remote rows without transport: {missing_transport[:5]}")
|
| 500 |
|
| 501 | op_transports: dict[tuple[str, str], set[str]] = {}
|
| 502 | for _, row in all_rows:
|
| 503 | transport = row.get("remote_transport") or row.get("workspace_transport")
|
| 504 | if transport:
|
| 505 | key = (str(row.get("subject")), str(row.get("operation")))
|
| 506 | op_transports.setdefault(key, set()).add(str(transport))
|
| 507 | mixed = {f"{subject} {op}": sorted(ts) for (subject, op), ts in op_transports.items() if len(ts) > 1}
|
| 508 | if mixed:
|
| 509 | failures.append(f"FAIL transports: operations span multiple transports: {mixed}")
|
| 510 | elif op_transports:
|
| 511 | notes.append(f"PASS transports: {len(op_transports)} remote-touching operations, one transport each")
|
| 512 |
|
| 513 | levels_by_file: dict[Path, set[str]] = {}
|
| 514 | for path, row in all_rows:
|
| 515 | level = row.get("instruction_level")
|
| 516 | if level is None and isinstance(row.get("agent"), dict):
|
| 517 | level = row["agent"].get("instruction_level")
|
| 518 | if level:
|
| 519 | levels_by_file.setdefault(path, set()).add(str(level))
|
| 520 | multi = {str(p): sorted(ls) for p, ls in levels_by_file.items() if len(ls) > 1}
|
| 521 | if multi:
|
| 522 | failures.append(f"FAIL levels: files mix instruction levels (never aggregate): {multi}")
|
| 523 | elif levels_by_file:
|
| 524 | notes.append("PASS levels: each agent result file holds a single instruction level")
|
| 525 |
|
| 526 | bad_skips = [
|
| 527 | (str(path), row.get("operation"))
|
| 528 | for path, row in all_rows
|
| 529 | if row_returncode(row) == 77 and not row.get("skip_reason")
|
| 530 | ]
|
| 531 | skip_count = sum(1 for _, row in all_rows if row_returncode(row) == 77)
|
| 532 | if bad_skips:
|
| 533 | failures.append(f"FAIL skips: returncode-77 rows without skip_reason: {bad_skips[:5]}")
|
| 534 | else:
|
| 535 | notes.append(f"PASS skips: {skip_count} skip rows, all carrying skip_reason (account for each in prose)")
|
| 536 |
|
| 537 | for path, row in all_rows:
|
| 538 | tail = (row.get("parallel_metrics") or {}).get("commit_latency_tail")
|
| 539 | if isinstance(tail, dict) and "samples" not in tail and "n" not in tail and "sample_count" not in tail:
|
| 540 | failures.append(f"FAIL tails: {path.name} {row.get('operation')} tail summary lacks sample count")
|
| 541 | break
|
| 542 | else:
|
| 543 | notes.append("PASS tails: tail summaries carry sample counts (or none present)")
|
| 544 |
|
| 545 | notes.append("MANUAL nulls: confirm summaries render null as 'unmeasured', never 0")
|
| 546 | notes.append("MANUAL prose: state transport asymmetries (oak network vs git local) next to every side-by-side")
|
| 547 |
|
| 548 | conformance_failures, conformance_notes = conformance_gate_lines(all_rows)
|
| 549 | failures += conformance_failures
|
| 550 | notes += conformance_notes
|
| 551 |
|
| 552 | for line in notes:
|
| 553 | print(line)
|
| 554 | for line in failures:
|
| 555 | print(line)
|
| 556 | print(f"\n{'GATE FAIL' if failures else 'GATE PASS'} ({len(failures)} failure(s), inputs: {len(paths)})")
|
| 557 | return 1 if failures else 0
|
| 558 |
|
| 559 |
|
| 560 | if __name__ == "__main__":
|
| 561 | raise SystemExit(main(sys.argv[1:]))
|