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