#!/usr/bin/env python3 """Independent-process native A/B and dated-window goldens; not an accuracy benchmark.""" from __future__ import annotations import argparse import hashlib import importlib import json import sys import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[2] def load(root: Path): for name in list(sys.modules): if name == "scripts" or name.startswith("scripts."): del sys.modules[name] sys.path.insert(0, str(root)) importlib.invalidate_caches() from scripts.rectification.api_service import score_candidates from scripts.research.probe_supply_after_six import request_from_case from scripts.rectification.decision_policy import indistinguishable_width_minutes return score_candidates, request_from_case, indistinguishable_width_minutes def canonical(value): return json.dumps(value, sort_keys=True, ensure_ascii=True, separators=(",", ":")).encode() def projection(result, width): new_fields = {"candidate_id", "candidate_date", "window_index", "window_offset_minutes", "segment_index", "cluster_intervals"} return { "candidate_scores": result["candidate_scores"], "matrix": result["event_contribution_matrix"], "decisions": [{key: value for key, value in row.items() if key not in new_fields} for row in result["candidate_decisions"]], "width": width(result["candidate_decisions"]), "confirmation_allowed": result["confirmation_allowed"], "selection_allowed": result["selection_allowed"], "representative_time": result["decision_receipt"]["representative_time"], } def worker(root: Path, dated: bool): score, make_request, width = load(root) dataset = ROOT / "references/real_case_calibration/minute_rectification_holdout_v3.json" result = [] for case in json.loads(dataset.read_text(encoding="utf-8"))["cases"][:3]: request = make_request(case) if dated: request["candidate_intervals"] = [{"start_at": f'{request["birth_date"]}T{request["start_time"]}', "end_at": f'{request["birth_date"]}T{request["end_time"]}'}] result.append(projection(score(request), width)) print(json.dumps(result)) def independent(root: Path, dated: bool): completed = subprocess.run([sys.executable, str(Path(__file__).resolve()), "--worker", str(root), *( ["--dated"] if dated else [])], cwd=root, check=True, capture_output=True, text=True, encoding="utf-8") return json.loads(completed.stdout) def main(): if "--worker" in sys.argv: worker(Path(sys.argv[sys.argv.index("--worker") + 1]), "--dated" in sys.argv) return parser = argparse.ArgumentParser() parser.add_argument("--baseline", type=Path, required=True) parser.add_argument("--output", type=Path, default=ROOT / "artifacts/midnight-date-anchor") parser.add_argument("--golden", action="store_true") parser.add_argument("--golden-path", type=Path, default=ROOT / "frontend/tests/fixtures/rectification-midnight-date-anchor.native.json") args = parser.parse_args() args.output.mkdir(parents=True, exist_ok=True) dataset = ROOT / "references/real_case_calibration/minute_rectification_holdout_v3.json" cases = json.loads(dataset.read_text(encoding="utf-8"))["cases"][:3] assert all(case["birth"]["source"]["rodden_rating"] == "AA" for case in cases) old = independent(args.baseline, False) current = independent(ROOT, True) comparisons = [] for case, before, after in zip(cases, old, current): fields = {key: canonical(before[key]) == canonical(after[key]) for key in before} comparisons.append({"case_id": case["case_id"], "source": case["birth"]["source"], "fields": fields, "before_sha256": hashlib.sha256(canonical(before)).hexdigest(), "after_sha256": hashlib.sha256(canonical(after)).hexdigest(), "candidate_minutes": len(after["candidate_scores"]), "public_clusters": len(after["decisions"]), "width": after["width"]}) print("current", case["case_id"], fields, flush=True) report = {"baseline": str(args.baseline), "dataset_sha256": hashlib.sha256(dataset.read_bytes()).hexdigest(), "same_machine_independent_processes": True, "python_executable": sys.executable, "numeric_tolerance": 0, "comparisons": comparisons, "excluded_fields": "new date/ordinal metadata; result/candidate IDs intentionally change with algorithm identity"} with (args.output / "independent-process-aa-ab.json").open("x", encoding="utf-8") as stream: json.dump(report, stream, indent=2) assert all(all(row["fields"].values()) for row in comparisons), "same-day native A/B changed" if args.golden: score, _, _ = load(ROOT) # Explicitly fictional birth/event facts; response is produced only by the unmocked native engine. request = {"birth_date": "2000-03-01", "start_time": "23:58", "end_time": "00:02", "lat": 0.0, "lon": 0.0, "tz": 0.0, "candidate_intervals": [{"start_at": "2000-02-29T23:58", "end_at": "2000-03-01T00:02"}], "events": [{"id": "00000000-0000-4000-8000-000000000001", "domain": "career", "event_kind": "career_entry", "date_start": "2020-01-01", "date_end": "2020-01-01", "precision": "day", "summary": "Fictional career entry for date-contract testing"}]} from scripts.rectification.contracts import normalize_rectification_request response = score(normalize_rectification_request(request)) target = args.golden_path with target.open("x", encoding="utf-8") as stream: json.dump({"provenance": {"kind": "unmocked_native_engine", "facts": "explicitly_fictional", "generator": "scripts/research/midnight_date_anchor_regression.py --baseline --golden", "algorithm": response["algorithm_version"]}, "request": request, "response": response}, stream, ensure_ascii=False, indent=2) print("golden", target, flush=True) if __name__ == "__main__": main()