| 1 | """Overnight Oak perf-improvement loop primitives.
|
| 2 |
|
| 3 | The loop is intentionally propose-only: scripts can select a target, verify a
|
| 4 | candidate, and append the ledger, but they never edit Oak and never merge.
|
| 5 | """
|
| 6 |
|
| 7 | from __future__ import annotations
|
| 8 |
|
| 9 | import json
|
| 10 | import os
|
| 11 | import statistics
|
| 12 | import subprocess
|
| 13 | import time
|
| 14 | from dataclasses import dataclass
|
| 15 | from datetime import datetime, timezone
|
| 16 | from pathlib import Path
|
| 17 | from typing import Any, Iterable, Sequence
|
| 18 |
|
| 19 | from oakbench.reporting import median
|
| 20 | from oakbench.rows import row_returncode
|
| 21 |
|
| 22 | SCORECARD_SCHEMA_VERSION = 1
|
| 23 | LEDGER_MARKER_START = "<!-- oak-perf-loop-entry"
|
| 24 | LEDGER_MARKER_END = "-->"
|
| 25 | DIAGNOSTIC_SUFFIXES = (".determinism", ".inforecall")
|
| 26 | SKIP_RETURNCODE = 77
|
| 27 |
|
| 28 | SCORECARD_EXIT_BETTER = 0
|
| 29 | SCORECARD_EXIT_NO_CHANGE = 10
|
| 30 | SCORECARD_EXIT_REGRESSED = 20
|
| 31 |
|
| 32 |
|
| 33 | def load_jsonl(paths: Sequence[Path]) -> list[dict[str, Any]]:
|
| 34 | rows: list[dict[str, Any]] = []
|
| 35 | for path in paths:
|
| 36 | with path.open() as fh:
|
| 37 | for line_number, raw in enumerate(fh, start=1):
|
| 38 | line = raw.strip()
|
| 39 | if not line:
|
| 40 | continue
|
| 41 | try:
|
| 42 | row = json.loads(line)
|
| 43 | except json.JSONDecodeError as exc:
|
| 44 | raise SystemExit(f"{path}:{line_number}: invalid JSON: {exc}") from exc
|
| 45 | if not isinstance(row, dict):
|
| 46 | raise SystemExit(f"{path}:{line_number}: expected JSON object")
|
| 47 | rows.append(row)
|
| 48 | return rows
|
| 49 |
|
| 50 |
|
| 51 | def write_json(path: Path, payload: dict[str, Any]) -> None:
|
| 52 | path.parent.mkdir(parents=True, exist_ok=True)
|
| 53 | path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
| 54 |
|
| 55 |
|
| 56 | def scorecard_exit_code(verdict: str) -> int:
|
| 57 | if verdict == "better":
|
| 58 | return SCORECARD_EXIT_BETTER
|
| 59 | if verdict == "no-change":
|
| 60 | return SCORECARD_EXIT_NO_CHANGE
|
| 61 | if verdict == "regressed":
|
| 62 | return SCORECARD_EXIT_REGRESSED
|
| 63 | raise ValueError(f"unknown scorecard verdict: {verdict}")
|
| 64 |
|
| 65 |
|
| 66 | def row_lane(row: dict[str, Any]) -> str:
|
| 67 | for key in ("lane", "benchmark_lane", "measurement_lane"):
|
| 68 | if row.get(key):
|
| 69 | return str(row[key])
|
| 70 | if "workflow" in row or "phase" in row:
|
| 71 | return "workflow"
|
| 72 | if "contention_mode" in row or "workers" in row:
|
| 73 | return "contention"
|
| 74 | if all(key in row for key in ("profile", "scenario", "operation", "subject", "subject_kind")):
|
| 75 | return "core"
|
| 76 | return "unknown"
|
| 77 |
|
| 78 |
|
| 79 | def scorecard_key(row: dict[str, Any]) -> tuple[str, str | None, str, str]:
|
| 80 | profile = row.get("profile")
|
| 81 | return (
|
| 82 | row_lane(row),
|
| 83 | str(profile) if profile is not None else None,
|
| 84 | str(row.get("scenario") or ""),
|
| 85 | str(row.get("operation") or ""),
|
| 86 | )
|
| 87 |
|
| 88 |
|
| 89 | def _successful_latency_rows(rows: Iterable[dict[str, Any]], subject: str) -> list[dict[str, Any]]:
|
| 90 | return [
|
| 91 | row
|
| 92 | for row in rows
|
| 93 | if str(row.get("subject")) == subject
|
| 94 | and row_returncode(row) == 0
|
| 95 | and not bool(row.get("skipped"))
|
| 96 | and isinstance(row.get("elapsed_ms"), (int, float))
|
| 97 | ]
|
| 98 |
|
| 99 |
|
| 100 | def _transport_signature(rows: list[dict[str, Any]]) -> tuple[str, ...]:
|
| 101 | values = {
|
| 102 | str(row.get("remote_transport") or row.get("transport") or "local")
|
| 103 | for row in rows
|
| 104 | if row.get("remote_transport") is not None or row.get("transport") is not None
|
| 105 | }
|
| 106 | if not values:
|
| 107 | return ("local",)
|
| 108 | return tuple(sorted(values))
|
| 109 |
|
| 110 |
|
| 111 | def _entry_id(entry: dict[str, Any]) -> str:
|
| 112 | profile = entry.get("profile")
|
| 113 | profile_part = "" if profile is None else f"{profile}:"
|
| 114 | return f"{entry['lane']}:{profile_part}{entry['scenario']}/{entry['operation']}"
|
| 115 |
|
| 116 |
|
| 117 | def _baseline_by_id(baseline: dict[str, Any] | None) -> dict[str, dict[str, Any]]:
|
| 118 | if not baseline:
|
| 119 | return {}
|
| 120 | return {_entry_id(entry): entry for entry in baseline.get("entries", []) if isinstance(entry, dict)}
|
| 121 |
|
| 122 |
|
| 123 | def build_scorecard(
|
| 124 | rows: list[dict[str, Any]],
|
| 125 | *,
|
| 126 | oak_subject: str,
|
| 127 | git_subject: str = "git",
|
| 128 | baseline: dict[str, Any] | None = None,
|
| 129 | change_threshold_pct: float = 3.0,
|
| 130 | ) -> dict[str, Any]:
|
| 131 | grouped: dict[tuple[str, str | None, str, str], list[dict[str, Any]]] = {}
|
| 132 | for row in rows:
|
| 133 | grouped.setdefault(scorecard_key(row), []).append(row)
|
| 134 |
|
| 135 | baseline_entries = _baseline_by_id(baseline)
|
| 136 | entries: list[dict[str, Any]] = []
|
| 137 | skipped: list[dict[str, Any]] = []
|
| 138 | any_better = False
|
| 139 | any_regressed = False
|
| 140 |
|
| 141 | for key, group in sorted(grouped.items()):
|
| 142 | lane, profile, scenario, operation = key
|
| 143 | if not scenario or not operation:
|
| 144 | continue
|
| 145 | oak_rows = _successful_latency_rows(group, oak_subject)
|
| 146 | git_rows = _successful_latency_rows(group, git_subject)
|
| 147 | if not oak_rows or not git_rows:
|
| 148 | continue
|
| 149 | oak_transport = _transport_signature(oak_rows)
|
| 150 | git_transport = _transport_signature(git_rows)
|
| 151 | if oak_transport != git_transport:
|
| 152 | skipped.append(
|
| 153 | {
|
| 154 | "lane": lane,
|
| 155 | "profile": profile,
|
| 156 | "scenario": scenario,
|
| 157 | "operation": operation,
|
| 158 | "reason": "transport_mismatch",
|
| 159 | "oak_transport": list(oak_transport),
|
| 160 | "git_transport": list(git_transport),
|
| 161 | }
|
| 162 | )
|
| 163 | continue
|
| 164 | oak_values = [float(row["elapsed_ms"]) for row in oak_rows]
|
| 165 | git_values = [float(row["elapsed_ms"]) for row in git_rows]
|
| 166 | oak_median = median(oak_values)
|
| 167 | git_median = median(git_values)
|
| 168 | if oak_median is None or git_median is None or git_median <= 0:
|
| 169 | continue
|
| 170 | ratio = oak_median / git_median
|
| 171 | entry: dict[str, Any] = {
|
| 172 | "lane": lane,
|
| 173 | "profile": profile,
|
| 174 | "scenario": scenario,
|
| 175 | "operation": operation,
|
| 176 | "metric": "elapsed_ms",
|
| 177 | "oak_subject": oak_subject,
|
| 178 | "git_subject": git_subject,
|
| 179 | "oak_median_ms": round(oak_median, 3),
|
| 180 | "git_median_ms": round(git_median, 3),
|
| 181 | "ratio": ratio,
|
| 182 | "n_oak": len(oak_values),
|
| 183 | "n_git": len(git_values),
|
| 184 | "transport": list(oak_transport),
|
| 185 | "verdict": "oak_slower" if ratio > 1.0 else ("oak_faster" if ratio < 1.0 else "parity"),
|
| 186 | }
|
| 187 | previous = baseline_entries.get(_entry_id(entry))
|
| 188 | if previous and isinstance(previous.get("ratio"), (int, float)):
|
| 189 | baseline_ratio = float(previous["ratio"])
|
| 190 | ratio_delta_pct = ((ratio - baseline_ratio) / baseline_ratio) * 100.0 if baseline_ratio else 0.0
|
| 191 | entry["baseline"] = {
|
| 192 | "ratio": round(baseline_ratio, 6),
|
| 193 | "ratio_delta_pct": round(ratio_delta_pct, 3),
|
| 194 | }
|
| 195 | if ratio_delta_pct <= -change_threshold_pct:
|
| 196 | entry["baseline"]["verdict"] = "better"
|
| 197 | any_better = True
|
| 198 | elif ratio_delta_pct >= change_threshold_pct:
|
| 199 | entry["baseline"]["verdict"] = "regressed"
|
| 200 | any_regressed = True
|
| 201 | else:
|
| 202 | entry["baseline"]["verdict"] = "no-change"
|
| 203 | entries.append(entry)
|
| 204 |
|
| 205 | gate_verdict = "regressed" if any_regressed else ("better" if any_better else "no-change")
|
| 206 | worst = max(
|
| 207 | (entry for entry in entries if entry.get("ratio") is not None),
|
| 208 | key=lambda entry: float(entry["ratio"]),
|
| 209 | default=None,
|
| 210 | )
|
| 211 | return {
|
| 212 | "schema_version": SCORECARD_SCHEMA_VERSION,
|
| 213 | "generated_at": datetime.now(timezone.utc).isoformat(),
|
| 214 | "oak_subject": oak_subject,
|
| 215 | "git_subject": git_subject,
|
| 216 | "metric": "elapsed_ms",
|
| 217 | "change_threshold_pct": change_threshold_pct,
|
| 218 | "summary": {
|
| 219 | "gate_verdict": gate_verdict,
|
| 220 | "entry_count": len(entries),
|
| 221 | "skipped_comparison_count": len(skipped),
|
| 222 | "worst": worst,
|
| 223 | },
|
| 224 | "entries": entries,
|
| 225 | "skipped_comparisons": skipped,
|
| 226 | }
|
| 227 |
|
| 228 |
|
| 229 | def load_scorecard(path: Path) -> dict[str, Any]:
|
| 230 | payload = json.loads(path.read_text())
|
| 231 | if not isinstance(payload, dict):
|
| 232 | raise SystemExit(f"{path}: expected scorecard JSON object")
|
| 233 | return payload
|
| 234 |
|
| 235 |
|
| 236 | def target_key(target: dict[str, Any]) -> str:
|
| 237 | profile = target.get("profile")
|
| 238 | profile_part = "" if profile is None else f"{profile}:"
|
| 239 | return f"{target.get('lane', 'core')}:{profile_part}{target['scenario']}/{target['operation']}"
|
| 240 |
|
| 241 |
|
| 242 | def is_diagnostic_operation(operation: str) -> bool:
|
| 243 | return operation.endswith(DIAGNOSTIC_SUFFIXES)
|
| 244 |
|
| 245 |
|
| 246 | def parse_ledger(path: Path) -> list[dict[str, Any]]:
|
| 247 | if not path.exists():
|
| 248 | return []
|
| 249 | lines = path.read_text().splitlines()
|
| 250 | entries: list[dict[str, Any]] = []
|
| 251 | index = 0
|
| 252 | while index < len(lines):
|
| 253 | if lines[index].strip() != LEDGER_MARKER_START:
|
| 254 | index += 1
|
| 255 | continue
|
| 256 | index += 1
|
| 257 | payload_lines: list[str] = []
|
| 258 | while index < len(lines) and lines[index].strip() != LEDGER_MARKER_END:
|
| 259 | payload_lines.append(lines[index])
|
| 260 | index += 1
|
| 261 | if index >= len(lines):
|
| 262 | break
|
| 263 | index += 1
|
| 264 | try:
|
| 265 | payload = json.loads("\n".join(payload_lines))
|
| 266 | except json.JSONDecodeError:
|
| 267 | continue
|
| 268 | if isinstance(payload, dict):
|
| 269 | entries.append(payload)
|
| 270 | return entries
|
| 271 |
|
| 272 |
|
| 273 | def no_payoff_targets(ledger_entries: list[dict[str, Any]]) -> set[str]:
|
| 274 | blocked: set[str] = set()
|
| 275 | for entry in ledger_entries:
|
| 276 | target = entry.get("target")
|
| 277 | if not isinstance(target, dict):
|
| 278 | continue
|
| 279 | verdict = str(entry.get("verdict") or entry.get("landed_or_parked") or "")
|
| 280 | reason = str(entry.get("reason") or "")
|
| 281 | if verdict in {"no-payoff", "parked_no_payoff"} or "did_not_pay" in reason or "no_payoff" in reason:
|
| 282 | blocked.add(target_key(target))
|
| 283 | return blocked
|
| 284 |
|
| 285 |
|
| 286 | def select_target(scorecard: dict[str, Any], ledger_entries: list[dict[str, Any]]) -> dict[str, Any] | None:
|
| 287 | blocked = no_payoff_targets(ledger_entries)
|
| 288 | candidates = []
|
| 289 | for entry in scorecard.get("entries", []):
|
| 290 | if not isinstance(entry, dict):
|
| 291 | continue
|
| 292 | if entry.get("metric") != "elapsed_ms":
|
| 293 | continue
|
| 294 | if is_diagnostic_operation(str(entry.get("operation") or "")):
|
| 295 | continue
|
| 296 | if float(entry.get("ratio") or 0.0) <= 1.0:
|
| 297 | continue
|
| 298 | if target_key(entry) in blocked:
|
| 299 | continue
|
| 300 | candidates.append(entry)
|
| 301 | if not candidates:
|
| 302 | return None
|
| 303 | candidates.sort(key=lambda entry: (-float(entry["ratio"]), str(entry["scenario"]), str(entry["operation"])))
|
| 304 | return candidates[0]
|
| 305 |
|
| 306 |
|
| 307 | def append_ledger(path: Path, entry: dict[str, Any]) -> None:
|
| 308 | path.parent.mkdir(parents=True, exist_ok=True)
|
| 309 | if not path.exists():
|
| 310 | path.write_text("# Oak Perf Loop Ledger\n\n")
|
| 311 | target = entry.get("target") if isinstance(entry.get("target"), dict) else {}
|
| 312 | title = target_key(target) if target else "unknown-target"
|
| 313 | verdict = entry.get("landed_or_parked") or entry.get("verdict") or "unknown"
|
| 314 | reason = entry.get("reason") or ""
|
| 315 | text = path.read_text()
|
| 316 | with path.open("a") as fh:
|
| 317 | if text and not text.endswith("\n"):
|
| 318 | fh.write("\n")
|
| 319 | fh.write(f"## {entry.get('timestamp', datetime.now(timezone.utc).isoformat())} — {title}\n\n")
|
| 320 | fh.write(f"- Verdict: {verdict}\n")
|
| 321 | if reason:
|
| 322 | fh.write(f"- Reason: {reason}\n")
|
| 323 | before = entry.get("before_after_medians") or {}
|
| 324 | if before.get("before_ms") is not None and before.get("after_ms") is not None:
|
| 325 | fh.write(
|
| 326 | "- Medians: "
|
| 327 | f"before {before.get('before_ms')} ms, after {before.get('after_ms')} ms"
|
| 328 | f", delta {before.get('delta_pct')}%\n"
|
| 329 | )
|
| 330 | elif before.get("before_ms") is not None:
|
| 331 | fh.write(f"- Current median: {before.get('before_ms')} ms\n")
|
| 332 | if entry.get("change_summary"):
|
| 333 | fh.write(f"- Change: {entry['change_summary']}\n")
|
| 334 | if entry.get("diagnosis"):
|
| 335 | fh.write(f"- Diagnosis: {entry['diagnosis']}\n")
|
| 336 | rejected = entry.get("levers_rejected") or []
|
| 337 | if rejected:
|
| 338 | fh.write("- Levers rejected:\n")
|
| 339 | for item in rejected:
|
| 340 | fh.write(f" - {item}\n")
|
| 341 | fh.write("\n")
|
| 342 | fh.write(LEDGER_MARKER_START + "\n")
|
| 343 | fh.write(json.dumps(entry, indent=2, sort_keys=True) + "\n")
|
| 344 | fh.write(LEDGER_MARKER_END + "\n\n")
|
| 345 |
|
| 346 |
|
| 347 | @dataclass(frozen=True)
|
| 348 | class ABSample:
|
| 349 | label: str
|
| 350 | elapsed_ms: float
|
| 351 | returncode: int = 0
|
| 352 | stderr: str = ""
|
| 353 |
|
| 354 |
|
| 355 | def run_interleaved_commands(
|
| 356 | before_command: Sequence[str],
|
| 357 | after_command: Sequence[str],
|
| 358 | *,
|
| 359 | cwd: Path,
|
| 360 | pairs: int,
|
| 361 | env: dict[str, str] | None = None,
|
| 362 | ) -> list[ABSample]:
|
| 363 | if pairs < 20:
|
| 364 | raise ValueError("interleaved A/B gate requires at least 20 before/after pairs")
|
| 365 | samples: list[ABSample] = []
|
| 366 | from oakbench.environment import host_home_environment
|
| 367 | run_env = os.environ.copy()
|
| 368 | if env:
|
| 369 | run_env.update(env)
|
| 370 | run_env = host_home_environment(run_env)
|
| 371 | for _ in range(pairs):
|
| 372 | for label, command in (("before", before_command), ("after", after_command)):
|
| 373 | start = time.perf_counter()
|
| 374 | proc = subprocess.run(
|
| 375 | list(command),
|
| 376 | cwd=cwd,
|
| 377 | env=run_env,
|
| 378 | stdout=subprocess.DEVNULL,
|
| 379 | stderr=subprocess.PIPE,
|
| 380 | text=True,
|
| 381 | check=False,
|
| 382 | )
|
| 383 | samples.append(
|
| 384 | ABSample(
|
| 385 | label=label,
|
| 386 | elapsed_ms=(time.perf_counter() - start) * 1000.0,
|
| 387 | returncode=proc.returncode,
|
| 388 | stderr=(proc.stderr or "")[-1000:],
|
| 389 | )
|
| 390 | )
|
| 391 | return samples
|
| 392 |
|
| 393 |
|
| 394 | def evaluate_interleaved_ab(
|
| 395 | samples: Sequence[ABSample],
|
| 396 | *,
|
| 397 | min_pairs: int = 20,
|
| 398 | min_improvement_pct: float = 3.0,
|
| 399 | ) -> dict[str, Any]:
|
| 400 | if len(samples) < min_pairs * 2:
|
| 401 | return {
|
| 402 | "passed": False,
|
| 403 | "reason": f"need at least {min_pairs} before/after pairs",
|
| 404 | "sample_count": len(samples),
|
| 405 | }
|
| 406 | for index, sample in enumerate(samples):
|
| 407 | expected = "before" if index % 2 == 0 else "after"
|
| 408 | if sample.label != expected:
|
| 409 | return {"passed": False, "reason": "samples are not strictly alternating before/after"}
|
| 410 | if sample.returncode != 0:
|
| 411 | return {
|
| 412 | "passed": False,
|
| 413 | "reason": f"{sample.label} command failed at sample {index}: {sample.stderr}",
|
| 414 | }
|
| 415 | before_values = [sample.elapsed_ms for sample in samples if sample.label == "before"]
|
| 416 | after_values = [sample.elapsed_ms for sample in samples if sample.label == "after"]
|
| 417 | if len(before_values) < min_pairs or len(after_values) < min_pairs:
|
| 418 | return {"passed": False, "reason": f"need {min_pairs} samples per side"}
|
| 419 | before_median = float(statistics.median(before_values))
|
| 420 | after_median = float(statistics.median(after_values))
|
| 421 | before_p25 = percentile(before_values, 0.25)
|
| 422 | after_p25 = percentile(after_values, 0.25)
|
| 423 | delta_pct = ((after_median - before_median) / before_median) * 100.0 if before_median else 0.0
|
| 424 | median_improved = delta_pct <= -min_improvement_pct
|
| 425 | consistency_improved = after_p25 < before_p25
|
| 426 | if median_improved and consistency_improved:
|
| 427 | reason = "target_metric_improved"
|
| 428 | elif median_improved:
|
| 429 | reason = "target_metric_improvement_not_consistent"
|
| 430 | else:
|
| 431 | reason = "target_metric_did_not_improve"
|
| 432 | return {
|
| 433 | "passed": median_improved and consistency_improved,
|
| 434 | "reason": reason,
|
| 435 | "before_ms": round(before_median, 3),
|
| 436 | "after_ms": round(after_median, 3),
|
| 437 | "delta_pct": round(delta_pct, 3),
|
| 438 | "before_p25_ms": round(before_p25, 3),
|
| 439 | "after_p25_ms": round(after_p25, 3),
|
| 440 | "before_n": len(before_values),
|
| 441 | "after_n": len(after_values),
|
| 442 | }
|
| 443 |
|
| 444 |
|
| 445 | def run_correctness_gate(oak_repo: Path, *, skip: bool = False) -> list[dict[str, Any]]:
|
| 446 | if skip:
|
| 447 | return [{"command": "skip", "returncode": 0, "skipped": True}]
|
| 448 | commands = [
|
| 449 | ["cargo", "build", "-p", "oakvcs-cli"],
|
| 450 | ["cargo", "test", "-p", "oakvcs-cli"],
|
| 451 | ["cargo", "clippy", "-p", "oakvcs-cli", "--all-targets", "--", "-D", "warnings"],
|
| 452 | ]
|
| 453 | results: list[dict[str, Any]] = []
|
| 454 | for command in commands:
|
| 455 | proc = subprocess.run(
|
| 456 | command,
|
| 457 | cwd=oak_repo,
|
| 458 | stdout=subprocess.PIPE,
|
| 459 | stderr=subprocess.STDOUT,
|
| 460 | text=True,
|
| 461 | check=False,
|
| 462 | )
|
| 463 | results.append(
|
| 464 | {
|
| 465 | "command": command,
|
| 466 | "returncode": proc.returncode,
|
| 467 | "output_tail": (proc.stdout or "")[-4000:],
|
| 468 | }
|
| 469 | )
|
| 470 | if proc.returncode != 0:
|
| 471 | break
|
| 472 | return results
|
| 473 |
|
| 474 |
|
| 475 | def correctness_passed(results: Sequence[dict[str, Any]]) -> bool:
|
| 476 | return all(int(result.get("returncode", 1)) == 0 for result in results)
|
| 477 |
|
| 478 |
|
| 479 | def percentile(values: Sequence[float], fraction: float) -> float:
|
| 480 | if not values:
|
| 481 | raise ValueError("percentile requires at least one value")
|
| 482 | if not 0.0 <= fraction <= 1.0:
|
| 483 | raise ValueError("percentile fraction must be between 0 and 1")
|
| 484 | ordered = sorted(values)
|
| 485 | if len(ordered) == 1:
|
| 486 | return float(ordered[0])
|
| 487 | position = (len(ordered) - 1) * fraction
|
| 488 | lower = int(position)
|
| 489 | upper = min(lower + 1, len(ordered) - 1)
|
| 490 | weight = position - lower
|
| 491 | return float((ordered[lower] * (1.0 - weight)) + (ordered[upper] * weight))
|
| 492 |
|
| 493 |
|
| 494 | def verification_entry(
|
| 495 | *,
|
| 496 | target: dict[str, Any],
|
| 497 | diagnosis: str,
|
| 498 | change_summary: str,
|
| 499 | correctness_results: Sequence[dict[str, Any]],
|
| 500 | ab_result: dict[str, Any],
|
| 501 | oak_gate_verdict: str = "MISSING",
|
| 502 | fleet_gate_verdict: str = "MISSING",
|
| 503 | levers_rejected: Sequence[str] = (),
|
| 504 | propose_mode: bool = True,
|
| 505 | ) -> dict[str, Any]:
|
| 506 | if not diagnosis.strip():
|
| 507 | status = "parked"
|
| 508 | reason = "missing_diagnosis_measurements"
|
| 509 | elif not correctness_passed(correctness_results):
|
| 510 | status = "parked"
|
| 511 | reason = "correctness_gate_failed"
|
| 512 | elif not ab_result.get("passed"):
|
| 513 | status = "parked"
|
| 514 | reason = str(ab_result.get("reason") or "interleaved_ab_failed")
|
| 515 | elif oak_gate_verdict != "PASS":
|
| 516 | status = "parked"
|
| 517 | reason = "oak_efficiency_gate_regressed"
|
| 518 | elif fleet_gate_verdict != "PASS":
|
| 519 | status = "parked"
|
| 520 | reason = "fleet_reliability_gate_regressed"
|
| 521 | elif propose_mode:
|
| 522 | status = "parked"
|
| 523 | reason = "verified_propose_mode_human_review_required"
|
| 524 | else:
|
| 525 | status = "landed"
|
| 526 | reason = "verified"
|
| 527 | return {
|
| 528 | "schema_version": 1,
|
| 529 | "timestamp": datetime.now(timezone.utc).isoformat(),
|
| 530 | "target": {
|
| 531 | "lane": target.get("lane"),
|
| 532 | "profile": target.get("profile"),
|
| 533 | "scenario": target.get("scenario"),
|
| 534 | "operation": target.get("operation"),
|
| 535 | "ratio": target.get("ratio"),
|
| 536 | "oak_median_ms": target.get("oak_median_ms"),
|
| 537 | "git_median_ms": target.get("git_median_ms"),
|
| 538 | },
|
| 539 | "diagnosis": diagnosis,
|
| 540 | "change_summary": change_summary,
|
| 541 | "before_after_medians": {
|
| 542 | "before_ms": ab_result.get("before_ms"),
|
| 543 | "after_ms": ab_result.get("after_ms"),
|
| 544 | "delta_pct": ab_result.get("delta_pct"),
|
| 545 | },
|
| 546 | "correctness_gate": list(correctness_results),
|
| 547 | "oak_efficiency_gate": oak_gate_verdict,
|
| 548 | "fleet_reliability_gate": fleet_gate_verdict,
|
| 549 | "levers_rejected": list(levers_rejected),
|
| 550 | "landed_or_parked": status,
|
| 551 | "reason": reason,
|
| 552 | }
|
| 553 |
|
| 554 |
|
| 555 | def dry_run_entry(target: dict[str, Any]) -> dict[str, Any]:
|
| 556 | return verification_entry(
|
| 557 | target=target,
|
| 558 | diagnosis="dry-run: target selection and ledger plumbing exercised; no Rust edit attempted",
|
| 559 | change_summary="dry-run parked candidate",
|
| 560 | correctness_results=[{"command": "dry-run", "returncode": 0}],
|
| 561 | ab_result={
|
| 562 | "passed": False,
|
| 563 | "reason": "dry_run_no_candidate_branch",
|
| 564 | "before_ms": target.get("oak_median_ms"),
|
| 565 | "after_ms": None,
|
| 566 | "delta_pct": None,
|
| 567 | },
|
| 568 | levers_rejected=[],
|
| 569 | )
|