Carry explicit local date intervals instead of inferring the day from clock order. Cluster width, delivery, adoption, and reports keep the actual civil date; adopted date is stored separately from the reported birth_date. Algorithm identity is scoring-9 / spec-v5. Scoring weights, confirmation thresholds, and Skill version are unchanged. Isolated Linux final-3 gates passed; four pre-existing Python failures remain. This is not a production release.
109 lines
5.9 KiB
Python
109 lines
5.9 KiB
Python
#!/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 <baseline-worktree> --golden", "algorithm": response["algorithm_version"]}, "request": request, "response": response}, stream, ensure_ascii=False, indent=2)
|
|
print("golden", target, flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|