merge origin/staging into api disk cache retention (issue #60)
Independent Staging Quality Gate / validate (push) Successful in 13m18s
Independent Staging Quality Gate / publish (push) Successful in 3m37s

Docs-only conflicts resolved by keeping both; agent-loop rule A.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LutgzyJqNhPpZy1nFsJtN3
This commit is contained in:
Claude
2026-10-14 09:11:01 +08:00
64 changed files with 48295 additions and 172 deletions
@@ -50,7 +50,7 @@ def worker(root: Path, dated: bool):
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))
result.append(projection(score(request, include_internal=True), width))
print(json.dumps(result))
@@ -0,0 +1,257 @@
#!/usr/bin/env python3
"""Bench and canonical dump for the 2026-10-13 rectification scoring speedup.
Read-only aside from the dump files the caller asks for. Cross-process dumps
must be launched with PYTHONHASHSEED=0. The baseline tree has no
include_internal parameter; that call falls back to the old signature.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import statistics
import sys
import time
from datetime import date
from pathlib import Path
from typing import Any, Callable
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from scripts.rectification.api_service import block_scan, diagnostics, score_candidates
from scripts.rectification.contracts import normalize_rectification_request
from scripts.rectification import event_probes
from scripts.rectification import scoring_service
from scripts import active_rectification_event_engine as event_engine
FROZEN_TODAY = date(2026, 9, 16)
EVENT_SPECS = (
("education", "education_start", "2008-01-01", "2008-12-31", "year"),
("career", "career_entry", "2012-06-01", "2012-06-30", "month"),
("relocation", "relocation", "2014-03-01", "2014-03-31", "month"),
("relationship", "relationship_commitment", "2016-10-01", "2016-10-31", "month"),
("career", "promotion", "2018-01-01", "2018-12-31", "year"),
("finance", "asset_change", "2019-05-01", "2019-05-31", "month"),
("family", "family_event", "2020-08-01", "2020-08-31", "month"),
("health", "self_health_event", "2022-01-01", "2022-12-31", "year"),
)
class _FrozenDate(date):
@classmethod
def today(cls) -> date:
return FROZEN_TODAY
def _events() -> list[dict[str, Any]]:
rows = []
for index, (domain, kind, start, end, precision) in enumerate(EVENT_SPECS, start=1):
rows.append({
"id": f"00000000-0000-4000-8000-{index:012d}",
"domain": domain,
"event_kind": kind,
"date_start": start,
"date_end": end,
"precision": precision,
"summary": "虚构",
})
return rows
def make_request(start: str, end: str, **extra: Any) -> dict[str, Any]:
body = {
"birth_date": "1990-01-01",
"start_time": start,
"end_time": end,
"lat": 39.9,
"lon": 116.4,
"tz": 8,
"events": _events(),
**extra,
}
return normalize_rectification_request(body, today=FROZEN_TODAY)
def _strip(value: Any, keys: set[str]) -> Any:
if isinstance(value, dict):
return {key: _strip(item, keys) for key, item in value.items() if key not in keys}
if isinstance(value, list):
return [_strip(item, keys) for item in value]
return value
def _canonical(value: Any) -> bytes:
return json.dumps(value, sort_keys=True, ensure_ascii=True, separators=(",", ":")).encode("ascii")
def _score(request: dict[str, Any]) -> dict[str, Any]:
try:
return score_candidates(request, include_internal=True)
except TypeError:
return score_candidates(request)
def _call(kind: str, request: dict[str, Any], *, internal: bool = False) -> dict[str, Any]:
if kind == "score":
# Bench times the product call. Dump asks for the internal matrix so
# the baseline tree, which always returned it, stays comparable.
if internal:
return _score(request)
return score_candidates(request)
if kind == "block":
return block_scan(request)
if kind == "diagnostics":
return diagnostics(request)
raise ValueError(kind)
def scenarios() -> list[dict[str, Any]]:
from tests.test_rectification_engine_memoization import public_score_request
rows = [
{"name": "12:00-12:20", "kind": "score", "request": make_request("12:00", "12:20")},
{"name": "11:45-12:15", "kind": "score", "request": make_request("11:45", "12:15")},
{"name": "11:00-13:00", "kind": "score", "request": make_request("11:00", "13:00")},
{"name": "23:20-00:40", "kind": "score", "request": make_request("23:20", "00:40")},
{"name": "declined-relationship-career", "kind": "score", "request": make_request(
"11:30", "12:30", declined_domains=["relationship", "career"],
)},
{"name": "refresh-probes-columns", "kind": "score", "request": make_request(
"11:30", "12:30", refresh_probes=True, column_times=["11:40", "12:10", "12:20"],
)},
{"name": "minute-step-2", "kind": "score", "request": make_request("10:00", "13:00", minute_step=2)},
{"name": "block-step-10", "kind": "block", "request": make_request("00:00", "23:59", minute_step=10)},
{"name": "block-step-5", "kind": "block", "request": make_request("10:00", "14:00", minute_step=5)},
{"name": "public-score-request", "kind": "score", "request": normalize_rectification_request(
public_score_request(), today=FROZEN_TODAY,
)},
{"name": "diagnostics-11:00-13:00", "kind": "diagnostics", "request": make_request("11:00", "13:00")},
]
return rows
def _strip_keys(kind: str) -> set[str]:
# diagnostics no longer repeats the receipt. Drop it on both sides so the
# remaining payload is what must stay byte-identical.
if kind == "diagnostics":
return {"column_compare_ms", "candidate_decision_receipt"}
return {"column_compare_ms"}
class _SegmentTimer:
def __init__(self) -> None:
self.totals = {
"guided_collect_windows": 0.0,
"merge_transition_proximity": 0.0,
"get_current_narayana_dasha": 0.0,
"derive_functional_benefic_malefic": 0.0,
"build_candidate_static_context": 0.0,
}
def wrap(self, owner: Any, name: str, key: str) -> Callable[[], None]:
original = getattr(owner, name)
def wrapped(*args: Any, **kwargs: Any) -> Any:
started = time.perf_counter()
try:
return original(*args, **kwargs)
finally:
self.totals[key] += time.perf_counter() - started
setattr(owner, name, wrapped)
def restore() -> None:
setattr(owner, name, original)
return restore
def _median(samples: list[float]) -> float:
return round(statistics.median(samples), 4)
def bench() -> dict[str, Any]:
jobs = [
("12:00-12:20", "score", make_request("12:00", "12:20")),
("11:45-12:15", "score", make_request("11:45", "12:15")),
("11:00-13:00", "score", make_request("11:00", "13:00")),
("block-step-10", "block", make_request("00:00", "23:59", minute_step=10)),
]
rows = []
event_probes.date = _FrozenDate
try:
for name, kind, request in jobs:
samples = []
segments = []
sizes = []
counts = []
for _ in range(5):
timer = _SegmentTimer()
restores = [
timer.wrap(event_probes, "guided_collect_windows", "guided_collect_windows"),
timer.wrap(scoring_service, "merge_transition_proximity", "merge_transition_proximity"),
timer.wrap(event_engine.narayana_dasha, "get_current_narayana_dasha", "get_current_narayana_dasha"),
timer.wrap(event_engine.functional_benefics, "derive_functional_benefic_malefic", "derive_functional_benefic_malefic"),
timer.wrap(event_engine, "build_candidate_static_context", "build_candidate_static_context"),
]
started = time.perf_counter()
try:
result = _call(kind, request)
finally:
for restore in restores:
restore()
elapsed = time.perf_counter() - started
samples.append(elapsed)
segments.append({key: round(value, 4) for key, value in timer.totals.items()})
sizes.append(len(_canonical(_strip(result, _strip_keys(kind)))))
counts.append(result.get("candidate_count") or len(result.get("candidate_scores") or []))
median_segments: dict[str, float] = {}
for key in segments[0]:
median_segments[key] = _median([item[key] for item in segments])
rows.append({
"window": name,
"candidate_count": counts[0],
"median_seconds": _median(samples),
"segment_median_seconds": median_segments,
"response_bytes": int(statistics.median(sizes)),
})
finally:
event_probes.date = date
return {"runs": 5, "rows": rows}
def dump(out_dir: Path) -> list[dict[str, str]]:
out_dir.mkdir(parents=True, exist_ok=True)
written = []
event_probes.date = _FrozenDate
try:
for row in scenarios():
result = _call(row["kind"], row["request"], internal=True)
payload = _strip(result, _strip_keys(row["kind"]))
raw = _canonical(payload)
digest = hashlib.sha256(raw).hexdigest()
path = out_dir / f"{row['name'].replace(':', '')}.json"
path.write_bytes(raw)
written.append({"name": row["name"], "sha256": digest, "bytes": str(len(raw))})
print(f"{row['name']}\t{digest}\t{len(raw)}")
finally:
event_probes.date = date
return written
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("command", choices=("bench", "dump"))
parser.add_argument("--out", type=Path, default=ROOT / "artifacts" / "rect-scoring-perf-20261013")
args = parser.parse_args()
if args.command == "bench":
print(json.dumps(bench(), ensure_ascii=True, indent=2))
else:
dump(args.out)
if __name__ == "__main__":
main()
+5 -2
View File
@@ -30,8 +30,11 @@ from scripts.research.sealed_holdout_rerun import (
OFFSETS = (-30, -20, -15, -10, -8, -5, -3, 0, 3, 5, 8, 10, 15, 20, 30)
RADII = (15, 30, 60)
MINUTE_STEP = 1
FREEZE = ROOT / "docs/research/reported_offset_ephemeris_nutation_2026_10_08.freeze.json"
REPORT = ROOT / "docs/research/reported_offset_ephemeris_nutation_2026_10_08.json"
# 原值: docs/research/reported_offset_ephemeris_nutation_2026_10_08.{freeze.json,json}
# 新值: docs/research/reported_offset_scoring_perf_2026_10_13.{freeze.json,json}
# 原因: BUG-1323~1325 改了冻结身份文件,输出不变,ERR-110 另存新记录
FREEZE = ROOT / "docs/research/reported_offset_scoring_perf_2026_10_13.freeze.json"
REPORT = ROOT / "docs/research/reported_offset_scoring_perf_2026_10_13.json"
LEGACY_REPORT = ROOT / "docs/research/reported_offset_2026_09_20.json"
+5 -2
View File
@@ -27,8 +27,11 @@ from scripts.minute_rectification_feature_facts_v4 import build_feature_fact_row
from scripts.minute_rectification_holdout_validator import validate
DATASET = ROOT / "references/real_case_calibration/minute_rectification_holdout_v3.json"
FREEZE = ROOT / "docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.freeze.json"
REPORT = ROOT / "docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.json"
# 原值: docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.{freeze.json,json}
# 新值: docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.{freeze.json,json}
# 原因: BUG-1323~1325 改了冻结身份文件,输出不变,ERR-110 另存新记录
FREEZE = ROOT / "docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.freeze.json"
REPORT = ROOT / "docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.json"
LEGACY_REPORT = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.json"
ARCHIVE = ROOT / "docs/research/history/rectification_pre_cross_midnight_2026_09_20"
PRODUCTION_FILES = [