Files
Jyotisha/tests/test_rectification_engine_convergence.py
T
Jesse_Chen 8743dcb105
Independent Staging Quality Gate / validate (push) Successful in 11m56s
Independent Staging Quality Gate / publish (push) Has been cancelled
fix(rectification): separate adjacent minutes with transition proximity
Day-level events now score Vimshottari/Narayana transition closeness so
nearby candidate minutes can diverge, with gated quality probes and
answer-prior ranking so high-base-rate existence questions stay out.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 17:41:06 +08:00

421 lines
15 KiB
Python

"""Round 2 engine-convergence invariants (TASK-rectification-engine-convergence-20260901)."""
from __future__ import annotations
import unittest
from datetime import date, datetime
from pathlib import Path
from scripts.rectification.candidate_contrast import distinguish_contract_errors
from scripts.rectification.contracts import normalize_rectification_request
from scripts.rectification.event_probes import (
_apply_prior_ranking,
_dominant_existence_prior,
_partition_ranked_probes,
_vim_start_dates,
discriminating_event_probes,
event_clarification_probes,
)
from scripts.rectification.refinement_packet import build_refinement_packet, window_scan
from scripts.rectification.scoring_service import build_event_contribution_matrix
REPO_ROOT = Path(__file__).resolve().parents[1]
PLANETS = {
"Sun": 12.0,
"Moon": 100.0,
"Mars": 40.0,
"Mercury": 20.0,
"Jupiter": 80.0,
"Venus": 50.0,
"Saturn": 200.0,
"Rahu": 310.0,
"Ketu": 130.0,
}
GRID_TIMES = [
f"{4 + (45 + offset) // 60:02d}:{(45 + offset) % 60:02d}"
for offset in range(31)
]
SPLIT_EVENT_ID = "00000000-0000-4000-8000-000000000003"
def _varga(asc: int, planet_sign: int) -> dict:
return {
"Ascendant": {"sign_idx": asc},
**{name: {"sign_idx": planet_sign} for name in PLANETS},
}
def _context(
time: str,
*,
moon: float = 100.0,
d4_asc: int = 1,
d9_asc: int = 1,
d10_asc: int = 1,
d12_asc: int = 1,
d24_asc: int = 1,
sun_house: int = 10,
) -> dict:
hour, minute = (int(part) for part in time.split(":"))
planets = {**PLANETS, "Moon": moon}
natal_planets = {
name: {"house": sun_house if name != "Moon" else 4, "lon": lon}
for name, lon in planets.items()
}
return {
"candidate_at": datetime(1997, 8, 8, hour, minute),
"chart": {"ascendant": {"lon": 10.0, "sign": "Aries"}, "planets": natal_planets},
"planet_longitudes": dict(planets),
"ascendant_index": 0,
"varga_charts": {
"D4": _varga(d4_asc, 1),
"D9": _varga(d9_asc, 1),
"D10": _varga(d10_asc, 1),
"D5": _varga(1, 1),
"D24": _varga(d24_asc, 1),
"D12": _varga(d12_asc, 1),
"D7": _varga(1, 1),
"D3": _varga(1, 1),
},
"arudha_padas": {},
"feature": {
"time": time,
"ascendant_sign_index": 0,
"varga_ascendants": {
"D4": d4_asc, "D9": d9_asc, "D10": d10_asc, "D5": 1, "D24": d24_asc, "D12": d12_asc,
},
},
}
def _accident_events() -> list[dict]:
return [
{
"id": "00000000-0000-4000-8000-000000000001",
"domain": "education",
"event_kind": "education_start",
"date_start": "2016-01-01",
"date_end": "2016-12-31",
"precision": "year",
"summary": "上大学",
},
{
"id": "00000000-0000-4000-8000-000000000002",
"domain": "relationship",
"event_kind": "relationship_start",
"date_start": "2024-05-01",
"date_end": "2024-05-31",
"precision": "month",
"summary": "开始认真交往",
},
{
"id": SPLIT_EVENT_ID,
"domain": "relationship",
"event_kind": "relationship_end",
"date_start": "2024-08-08",
"date_end": "2024-08-08",
"precision": "day",
"summary": "分手",
},
{
"id": "00000000-0000-4000-8000-000000000004",
"domain": "career",
"event_kind": "career_entry",
"date_start": "2020-04-01",
"date_end": "2020-04-30",
"precision": "month",
"summary": "实习入职",
},
{
"id": "00000000-0000-4000-8000-000000000005",
"domain": "career",
"event_kind": "career_exit",
"date_start": "2020-10-01",
"date_end": "2020-10-31",
"precision": "month",
"summary": "实习结束离职",
},
]
def _accident_request() -> dict:
return normalize_rectification_request(
{
"birth_date": "1997-08-08",
"start_time": "04:45",
"end_time": "05:15",
"lat": 36.420487,
"lon": 114.209936,
"tz": 8,
"events": _accident_events(),
},
today=date(2026, 8, 22),
)
def _equal_rows(payload: dict) -> list[dict]:
event = payload["events"][0]
return [
{
"time": time,
"score": 10,
"evidence": [{
"event_id": event["id"],
"domain": event["domain"],
"candidate_time": time,
"rule_ids": ["vim_md_domain_house"],
"points": 10,
}],
"missing_layers": [],
}
for time in GRID_TIMES
]
def _moons_with_split_proximity() -> tuple[float, float]:
event_at = date(2024, 8, 8)
ranked: list[tuple[int, float]] = []
for moon in (100.0, 100.5, 101.0, 103.0, 110.0):
starts = _vim_start_dates("1997-08-08", moon, 2023, 2025)
if not starts:
continue
nearest = min(abs((item - event_at).days) for item in starts)
ranked.append((nearest, moon))
ranked.sort()
if len(ranked) < 2 or ranked[0][0] == ranked[-1][0]:
raise AssertionError("fixture moons do not split AD/PD proximity")
return ranked[0][1], ranked[-1][1]
def _probe_events(*, education_count: int = 2, d24_split: bool = True) -> tuple[dict, dict]:
events = [
{
"id": f"00000000-0000-4000-8000-{index:012d}",
"domain": "education",
"event_kind": "education_start",
"summary": "入学",
"date": f"{2015 + index}-09-01",
"precision": "month",
}
for index in range(1, education_count + 1)
]
events.extend([
{
"id": "00000000-0000-4000-8000-000000000011",
"domain": "career",
"event_kind": "career_entry",
"summary": "入职",
"date": "2018-07-01",
"precision": "month",
},
{
"id": "00000000-0000-4000-8000-000000000012",
"domain": "career",
"event_kind": "career_change",
"summary": "换岗",
"date": "2020-04-01",
"precision": "month",
},
{
"id": "00000000-0000-4000-8000-000000000013",
"domain": "relationship",
"event_kind": "relationship_start",
"summary": "相识",
"date": "2021-08-01",
"precision": "month",
},
])
late = 2 if d24_split else 1
built = {
"static_contexts": [
_context("05:00", d24_asc=1, d12_asc=1, d9_asc=1, d10_asc=1),
_context("05:07", d24_asc=late, d12_asc=late, d9_asc=late, d10_asc=late),
]
}
return {"birth_date": "1997-08-08", "events": events}, built
class EngineConvergenceProximityTests(unittest.TestCase):
def test_day_event_proximity_splits_adjacent_minutes_in_accident_shape(self) -> None:
from scripts.rectification.dasha_transition_proximity import (
DAY_MAX_POINTS,
score_transition_proximity,
)
closer_moon, farther_moon = _moons_with_split_proximity()
event_at = date(2024, 8, 8)
closer_starts = _vim_start_dates("1997-08-08", closer_moon, 2023, 2025)
farther_starts = _vim_start_dates("1997-08-08", farther_moon, 2023, 2025)
closer_delta = min(abs((item - event_at).days) for item in closer_starts)
farther_delta = min(abs((item - event_at).days) for item in farther_starts)
closer_points = score_transition_proximity(
event_date=event_at,
precision="day",
vim_starts=closer_starts,
narayana_starts=[],
)
farther_points = score_transition_proximity(
event_date=event_at,
precision="day",
vim_starts=farther_starts,
narayana_starts=[],
)
self.assertLessEqual(float(closer_points["points"]), DAY_MAX_POINTS)
self.assertNotEqual(closer_points["points"], farther_points["points"])
self.assertEqual(
closer_points["points"] > farther_points["points"],
closer_delta < farther_delta,
)
self.assertTrue(
any(str(rule).startswith("vim_transition_proximity_") for rule in closer_points["rule_ids"])
)
contexts = [
_context(time, moon=closer_moon if time == "05:00" else farther_moon)
for time in GRID_TIMES
]
request = _accident_request()
built = build_event_contribution_matrix(
request,
row_provider=_equal_rows,
static_contexts=contexts,
)
self.assertEqual(len(built["candidate_times"]), 31)
cell_0500 = built["matrix"][SPLIT_EVENT_ID]["05:00"]
cell_0507 = built["matrix"][SPLIT_EVENT_ID]["05:07"]
self.assertNotEqual(cell_0500["points"], cell_0507["points"])
self.assertEqual(
cell_0500["points"] > cell_0507["points"],
closer_delta < farther_delta,
)
self.assertTrue(
any("transition_proximity" in str(rule) for rule in cell_0500["rule_ids"])
or any("transition_proximity" in str(rule) for rule in cell_0507["rule_ids"])
)
self.assertLessEqual(abs(cell_0500["points"] - cell_0507["points"]), DAY_MAX_POINTS)
def test_algorithm_and_policy_versions_change_with_proximity_semantics(self) -> None:
from scripts.rectification.decision_policy import POLICY_VERSION
from scripts.rectification.scoring_service import ALGORITHM_VERSION
self.assertNotEqual(ALGORITHM_VERSION, "rectification-v5-matrix-scoring-6")
self.assertNotEqual(POLICY_VERSION, "rectification-candidate-policy-v2")
class EngineConvergenceProbeTests(unittest.TestCase):
def test_anchored_quality_outranks_family_existence_and_drops_dominant_priors(self) -> None:
request, built = _probe_events()
probes = discriminating_event_probes(
request,
built,
scan=window_scan(built),
candidate_times=["05:00", "05:07"],
representative_time="05:00",
today=date(2026, 8, 22),
)
quality = [item for item in probes if item.get("source") == "known_event_quality"]
self.assertTrue(quality)
self.assertEqual(quality[0]["role"], "distinguish")
self.assertTrue(quality[0].get("target_evidence_id"))
self.assertFalse(distinguish_contract_errors(quality[0]))
ranked_family = _apply_prior_ranking({
"domain": "family",
"source": "dasha_boundary",
"choice_kind": "existence",
"information_gain": float(quality[0].get("raw_split_gain") or quality[0].get("information_gain") or 0),
"semantic_key": "family.2024.existence",
"year": 2024,
"window_span_years": 3,
"role": "distinguish",
"phase": "candidate_discriminator",
"candidate_ids": ["05:00", "05:07"],
"expected_outcomes": [
{"answer_class": "yes", "supports": ["05:00"], "conflicts": ["05:07"]},
{"answer_class": "no", "supports": ["05:07"], "conflicts": ["05:00"]},
],
})
self.assertTrue(_dominant_existence_prior(ranked_family, ranked_family["answer_priors"]))
_, ranked_dropped = _partition_ranked_probes([quality[0], ranked_family])
self.assertTrue(
any(item.get("reason") == "dominant_answer_prior" for item in ranked_dropped),
ranked_dropped,
)
packet = build_refinement_packet(
request,
built,
representative_time="05:00",
candidate_times=["05:00", "05:07"],
)
dropped = list(packet.get("dropped_probes") or []) + ranked_dropped
self.assertTrue(
any(item.get("reason") == "dominant_answer_prior" for item in dropped),
dropped,
)
self.assertFalse(
any(
item.get("choice_kind") == "existence"
and float(max((item.get("answer_priors") or {}).values() or [0])) > 0.8
for item in probes
)
)
def test_quality_distinguish_requires_varga_type_split_and_caps_at_two(self) -> None:
same_request, same_built = _probe_events(d24_split=False)
same_probes = discriminating_event_probes(
same_request,
same_built,
scan=window_scan(same_built),
candidate_times=["05:00", "05:07"],
representative_time="05:00",
today=date(2026, 8, 22),
)
self.assertFalse(any(item.get("source") == "known_event_quality" for item in same_probes))
clarification = event_clarification_probes(same_request)
self.assertTrue(any(item.get("source") == "known_event_quality" for item in clarification))
split_request, split_built = _probe_events(education_count=4, d24_split=True)
split_probes = discriminating_event_probes(
split_request,
split_built,
scan=window_scan(split_built),
candidate_times=["05:00", "05:07"],
representative_time="05:00",
today=date(2026, 8, 22),
)
quality = [item for item in split_probes if item.get("source") == "known_event_quality"]
self.assertTrue(quality)
self.assertLessEqual(len(quality), 2)
for item in quality:
self.assertEqual(item["role"], "distinguish")
self.assertTrue(item.get("target_evidence_id"))
self.assertTrue(item.get("display_date_label"))
self.assertEqual(item.get("choice_kind"), "event_quality")
self.assertFalse(distinguish_contract_errors(item))
class EngineConvergenceAnswerKeyTests(unittest.TestCase):
def test_scripts_and_frontend_have_no_answer_key_literals(self) -> None:
forbidden = ("target" + "_minute", "pl9_" + "1993", "regression" + "_only")
hits: list[str] = []
roots = (
REPO_ROOT / "scripts",
REPO_ROOT / "frontend" / "src",
REPO_ROOT / "frontend" / "tests",
)
skip_parts = {"node_modules", ".next", "dist"}
for root in roots:
for path in root.rglob("*"):
if not path.is_file() or path.suffix not in {".py", ".ts", ".tsx", ".js", ".mjs"}:
continue
if any(part in skip_parts for part in path.parts):
continue
text = path.read_text(encoding="utf-8", errors="ignore")
for token in forbidden:
if token in text:
hits.append(f"{path.relative_to(REPO_ROOT)}:{token}")
self.assertEqual(hits, [])
if __name__ == "__main__":
unittest.main()