from __future__ import annotations from datetime import datetime from kp_system import KP_LORDS from scripts.active_rectification_event_engine import ( build_candidate_static_context, compute_event_candidate_rows, ) from scripts.rectification.decision_policy import ( build_candidate_decisions, build_decision_receipt, build_technique_audit, ) from scripts.rectification.kp_cusp_observation import observe_kp_cusps from scripts.rectification.refinement_packet import window_scan def _request() -> dict: return { "birth_date": "1993-04-17", "start_time": "14:29", "end_time": "14:30", "lat": 36.683333, "lon": 114.35, "tz": 8.0, "events": [{ "id": "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5", "domain": "career", "date": "2019-07-01", "precision": "day", }], } def _score_request() -> dict: return { "events": [ { "id": "00000000-0000-4000-8000-000000000001", "domain": "career", "summary": "入职", "event_kind": "career_entry", "precision": "day", "date_start": "2016-09-15", "date_end": "2016-09-15", }, { "id": "00000000-0000-4000-8000-000000000002", "domain": "relationship", "summary": "开始一段关系", "event_kind": "relationship_start", "precision": "day", "date_start": "2018-03-01", "date_end": "2018-03-01", }, { "id": "00000000-0000-4000-8000-000000000003", "domain": "education", "summary": "毕业", "event_kind": "education_completion", "precision": "day", "date_start": "2015-06-01", "date_end": "2015-06-01", }, { "id": "00000000-0000-4000-8000-000000000004", "domain": "family", "summary": "家人变化", "event_kind": "family_event", "precision": "day", "date_start": "2020-01-01", "date_end": "2020-01-01", }, ] } def _diagnostics() -> dict: return { "leave_one_event_out_retention_rate": 1, "leave_one_domain_out_retention_rate": 1, "date_sensitivity_retention_rate": 1, "primary_secondary_margin_percent": 50, } def test_observe_kp_cusps_uses_placidus_not_equal_houses() -> None: context = build_candidate_static_context(_request(), datetime(1993, 4, 17, 14, 29)) feature = context["feature"] snapshot = feature["kp_cusps"] natal_houses = context["chart"]["houses"] assert snapshot["status"] == "executed" assert snapshot["house_system"] == "placidus" assert snapshot["ayanamsa"] == "krishnamurti" assert len(snapshot["houses"]) == 12 tenth = snapshot["houses"]["10"] equal_tenth = float(natal_houses["house_10"]["cusp_degree"]) assert abs(float(tenth["cusp_degree"]) - equal_tenth) > 0.05 assert tenth["sub_lord"] in KP_LORDS assert isinstance(feature["kp1_sub_index"], int) assert 0 <= feature["kp1_sub_index"] <= 8 assert "KP_cusps" not in feature["blocked_layers"] assert "KP_cusps" in feature["available_layers"] def test_kp_observation_is_absent_from_scoring_missing_layers() -> None: request = _request() context = build_candidate_static_context(request, datetime(1993, 4, 17, 14, 29)) rows = compute_event_candidate_rows(request, static_contexts=[context]) assert rows assert "KP_cusps" not in rows[0]["missing_layers"] assert context["feature"]["kp_cusps"]["status"] == "executed" def test_window_scan_emits_kp_sub_lord_changes_without_confirmation() -> None: scan = window_scan({ "static_contexts": [ {"feature": {"time": "05:13", "kp1_sub_index": 1, "kp4_sub_index": 2, "kp7_sub_index": 3, "kp10_sub_index": 4}}, {"feature": {"time": "05:14", "kp1_sub_index": 2, "kp4_sub_index": 2, "kp7_sub_index": 3, "kp10_sub_index": 4}}, ] }) meanings = [item["user_meaning"] for item in scan["transitions"]] assert "KP 1宫子主 在 05:14 发生变化" in meanings assert "KP 4宫子主 在 05:14 发生变化" not in meanings encoded = str(scan) assert "白羊" not in encoded assert "Aries" not in encoded assert scan["confirmation_allowed"] is False assert scan["unique_minute_claim"] is False def test_technique_audit_kp_executed_when_snapshot_present() -> None: rows = build_technique_audit( { "matrix": {}, "static_contexts": [{ "feature": { "time": "05:13", "kp_cusps": {"status": "executed", "house_system": "placidus", "ayanamsa": "krishnamurti"}, } }], }, house_table={"time": "05:13", "lagna": "金牛座"}, ) kp = next(row for row in rows if row["technique"] == "KP 宫头") assert kp["status"] == "executed" assert "政策跳过" not in kp["note"] assert "不计分" in kp["note"] def test_technique_audit_kp_blocked_is_honest_and_does_not_fail_propose() -> None: rows = build_technique_audit({"matrix": {}, "static_contexts": []}, house_table=None) kp = next(row for row in rows if row["technique"] == "KP 宫头") assert kp["status"] == "blocked" assert "政策跳过" not in kp["note"] decisions = build_candidate_decisions( [{"time": "05:13", "score": 20, "evidence": [], "missing_layers": []}, {"time": "05:14", "score": 8, "evidence": [], "missing_layers": []}], result_id="00000000-0000-4000-8000-000000000099", ) receipt = build_decision_receipt( _score_request(), decisions, {"missing_layers": [], "matrix": {}, "static_contexts": [{"feature": {"time": "05:13", "kp_cusps": {"status": "blocked"}}}]}, _diagnostics(), ) assert receipt["gates"]["required_layers"]["passed"] is True assert receipt["propose_allowed"] is True audit = next(row for row in receipt["technique_audit_table"] if row["technique"] == "KP 宫头") assert audit["status"] == "blocked" assert "政策跳过" not in audit["note"] def test_observe_kp_cusps_blocks_without_inventing_whole_sign_proxy() -> None: snapshot = observe_kp_cusps(None, 36.6, 114.3) assert snapshot["status"] == "blocked" assert snapshot["houses"] == {} polar = observe_kp_cusps(2451545.0, 80.0, 0.0) assert polar["status"] == "blocked" assert polar["houses"] == {}