#!/usr/bin/env python3 """Regression tests for MCP relationship event adjudication.""" from __future__ import annotations import sys from pathlib import Path from mcp_server import _collect_strict_evidence ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) import jyotish_engine # noqa: E402 SOURCE_LAYER_CONTEXT = { "dasha_timing_layer_used", "varga_strength_layer_used", "annual_special_layer_context", "modifier_obstacle_layer_used", } def _assert_context_contains(context: list[str], expected: set[str]) -> None: assert expected <= set(context) assert SOURCE_LAYER_CONTEXT <= set(context) def _base_relationship_result() -> dict: return { "modules": { "varga_full": {"D9_Navamsa": {"summary": "ok"}}, "special_lagnas": {"Upapada_Lagna": {"sign": "Libra", "lord": "Venus"}}, "jaimini": { "darakaraka": {"planet": "Venus", "house": 7}, "marriage_support": {"dk_7h_link": True}, }, "vivah_saham": {"sign": "Taurus", "house": 7}, "dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Moon"}}, "narayana_dasha": {"current_dasha": {"sign": "Libra", "lord": "Venus"}}, "dasa_convergence": { "domain_activations": { "marriage_partnership": {"convergence_level": "L1", "probability": "+15-20%"} } }, "argala": { "houses": { "house_7": { "net_result": "obstructed", "argala_count": 1, "virodhargala_count": 2, } } }, } } def test_relationship_jaimini_bridge_lifts_legal_marriage_label() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) assert strict["present_evidence"]["jaimini_marriage_support"] == { "level": "moderate", "signals": ["darakaraka_active", "dk_7h_link"], "source": "jaimini_bridge_v1", } assert strict["event_judgement"]["dominant_label"] == "legal_marriage" _assert_context_contains( strict["event_judgement"]["secondary_context"], { "darakaraka_active", "jaimini_support", "ul_support", "virodhargala_obstruction", "vedastro_range_scan_missing", }, ) def test_relationship_strict_contract_attaches_existing_interpretation_source_pack() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) source_pack = strict["present_evidence"]["interpretation_source_pack"] assert source_pack["status"] == "used" assert "darakaraka_ul_spouse_depth" in source_pack["template_registry"]["template_ids"] assert "rtn_high_order_d9" in source_pack["template_registry"]["template_ids"] assert "p1_p12" in source_pack["frameworks"] assert "house_framework" in source_pack["frameworks"] assert "raman_functional_house_judgment" in source_pack["frameworks"] assert source_pack["interpretation_source_inventory"]["status"] == "used" assert source_pack["qa_governance_layer"]["status"] == "available" assert source_pack["reader_validation_layer"]["status"] == "available" assert "references/open_source_sources/vedic-astro-skills/codex/skills/vedic-core/resources/qa_rules.md" in source_pack["qa_governance_layer"]["source_refs"] audit = strict["technique_audit_summary"] assert audit["interpretation_source_pack"]["source"] == "repo_existing_interpretation_sources" assert audit["mevg_global_web_evidence"]["required"] is True assert audit["real_case_calibration"]["required"] is True def test_relationship_jaimini_bridge_stays_context_only_when_d9_missing() -> None: result = _base_relationship_result() del result["modules"]["varga_full"]["D9_Navamsa"] strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["jaimini_marriage_support"] == { "level": "moderate", "signals": ["darakaraka_active", "dk_7h_link"], "source": "jaimini_bridge_v1", } assert strict["event_judgement"]["dominant_label"] is None _assert_context_contains( strict["event_judgement"]["secondary_context"], { "darakaraka_active", "jaimini_support", "ul_support", "virodhargala_obstruction", "vedastro_range_scan_missing", }, ) def test_relationship_jaimini_bridge_cannot_lift_legal_marriage_when_narayana_is_missing() -> None: result = _base_relationship_result() del result["modules"]["narayana_dasha"] strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["jaimini_marriage_support"] == { "level": "moderate", "signals": ["darakaraka_active", "dk_7h_link"], "source": "jaimini_bridge_v1", } assert strict["event_judgement"]["dominant_label"] is None assert "jaimini_support" in strict["event_judgement"]["secondary_context"] assert "public_formalization_candidate" not in strict["event_judgement"]["secondary_context"] def test_relationship_jaimini_bridge_uses_public_formalization_as_context_only_when_marriage_gate_is_open_but_label_support_missing() -> None: result = _base_relationship_result() del result["modules"]["vivah_saham"] del result["modules"]["dasa_convergence"] result["modules"]["external_activation"] = { "evidence_ledger": [ { "source": "vedastro_service_adapter_candidate", "operation": "range_scan", "domain": "marriage", "event_id": "public_relationship_window", "signal_key": "public_relationship_window", "score": 78, } ] } result["modules"]["synastry"] = { "total_score": 28.0, "max_score": 36.0, "is_approved": True, "additional_kutas": { "Mahendra": "good", "Vedha": "good", "Rajju": {"result": "good", "group": None, "effect": ""}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["event_judgement"]["dominant_label"] is None assert "jaimini_support" in strict["event_judgement"]["secondary_context"] assert "external_activation_support" in strict["event_judgement"]["secondary_context"] assert "synastry_support" in strict["event_judgement"]["secondary_context"] assert "public_formalization_candidate" in strict["event_judgement"]["secondary_context"] def test_relationship_argala_bridge_uses_seventh_house_as_modifier_only() -> None: result = _base_relationship_result() strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["argala_support"] == { "level": "obstructive", "target_house": 7, "source": "argala_house_bridge_v1", "signals": ["virodhargala_obstruction"], "raw": { "net_result": "obstructed", "argala_count": 1, "virodhargala_count": 2, }, } assert strict["event_judgement"]["dominant_label"] == "legal_marriage" assert "virodhargala_obstruction" in strict["event_judgement"]["secondary_context"] def test_relationship_collects_vedastro_range_scan_as_external_activation_context() -> None: result = _base_relationship_result() result["modules"]["external_activation"] = { "evidence_ledger": [ { "source": "vedastro_service_adapter_candidate", "operation": "range_scan", "domain": "marriage", "event_id": "jupiter_7h_window", "score": 72, } ] } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["external_activation"]["level"] == "moderate" assert strict["present_evidence"]["external_activation"]["source"] == "vedastro_service_adapter_candidate" assert "external_activation_support" in strict["event_judgement"]["secondary_context"] def test_relationship_external_activation_exposes_top_daily_window() -> None: result = _base_relationship_result() result["modules"]["vedastro_range_scan_result"] = { "backend": "vedastro_service_adapter_candidate", "status": "ok", "operation": "range_scan", "domain": "marriage", "evidence_ledger": [], "daily_windows": [{"date": "2026-08-02", "domain": "marriage", "score": 5, "event_count": 2}], "top_daily_window": {"date": "2026-08-02", "domain": "marriage", "score": 5, "event_count": 2}, "source_metadata": {}, } strict = _collect_strict_evidence("relationship", result) external = strict["present_evidence"]["external_activation"] assert external["top_daily_window"]["date"] == "2026-08-02" assert external["daily_windows"][0]["score"] == 5 def test_relationship_external_activation_derives_progress_day_signals() -> None: result = _base_relationship_result() result["modules"]["vedastro_range_scan_result"] = { "backend": "vedastro_service_adapter_candidate", "status": "ok", "operation": "range_scan", "domain": "marriage", "evidence_ledger": [], "daily_windows": [ { "date": "2026-08-02", "domain": "marriage", "score": 5, "confidence": "high", "event_count": 2, "signal_families": ["marriage_trigger"], "event_ids": ["GocharJupiterIn7th", "JupiterSupportsMarriageAxis"], "top_signal_label": "Jupiter in 7th marriage window", }, { "date": "2026-08-19", "domain": "marriage", "score": 2, "confidence": "medium", "event_count": 1, "signal_families": ["relationship_pressure"], "event_ids": ["SaturnAspect7thRelationshipWindow"], "top_signal_label": "Saturn aspecting 7th relationship window", }, ], "top_daily_window": { "date": "2026-08-02", "domain": "marriage", "score": 5, "confidence": "high", "event_count": 2, "signal_families": ["marriage_trigger"], "event_ids": ["GocharJupiterIn7th", "JupiterSupportsMarriageAxis"], "top_signal_label": "Jupiter in 7th marriage window", }, "source_metadata": {}, } strict = _collect_strict_evidence("relationship", result) external = strict["present_evidence"]["external_activation"] assert external["official_day_signals"][0]["date"] == "2026-08-02" assert external["official_day_signals"][0]["day_type"] == "progress" assert external["official_day_signals"][0]["summary"] == "婚恋推进日" assert external["official_day_signals"][1]["day_type"] == "risk" def test_relationship_strict_contract_exposes_official_primary_and_local_supplemental_layers() -> None: result = _base_relationship_result() result["modules"]["source_priority"] = {"mode": "vedastro_official_primary"} result["modules"]["vedastro_official_full_snapshot"] = { "status": "partial", "available": True, "official_chart": {"planets": {"Sun": {}}, "ascendant": {"sign": "Leo"}}, "section_statuses": {"chart_core": "ok", "dasha_all": "ok", "events_overview": "partial"}, } strict = _collect_strict_evidence("relationship", result) assert strict["official_primary_evidence"]["chart_core"]["status"] == "ok" assert strict["official_primary_evidence"]["dasha"]["status"] == "ok" assert strict["local_supplemental_evidence"]["upapada_lagna"]["role"] == "required_local_supplement" assert strict["local_supplemental_evidence"]["darakaraka"]["role"] == "required_local_supplement" assert isinstance(strict["fallback_used"], list) assert isinstance(strict["blocked_items"], list) assert isinstance(strict["conflicts"], list) def test_relationship_strict_contract_exposes_adjudication_stages_and_multi_reference_summary() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) assert strict["adjudication_stages"]["promise"]["status"] in {"present", "weak", "missing"} assert strict["adjudication_stages"]["activation"]["required_timing_systems"] == ["Vimshottari", "Narayana"] assert strict["adjudication_stages"]["label"]["value"] == strict["event_judgement"]["dominant_label"] summary = strict["multi_reference_reading_summary"] assert "root_frame" in summary assert "divisional_frame" in summary assert "visibility_frame" in summary assert "karaka_frame" in summary assert "timing_frame" in summary assert "modifier_frame" in summary assert "conflict_frame" in summary def test_relationship_strict_contract_exposes_monthly_adjudication_summary() -> None: result = _base_relationship_result() result["modules"]["vedastro_range_scan_result"] = { "backend": "vedastro_service_adapter_candidate", "status": "ok", "operation": "range_scan", "domain": "marriage", "evidence_ledger": [], "daily_windows": [ { "date": "2026-08-02", "domain": "marriage", "score": 5, "confidence": "high", "event_count": 2, "signal_families": ["marriage_trigger"], "event_ids": ["GocharJupiterIn7th", "JupiterSupportsMarriageAxis"], "top_signal_label": "Jupiter in 7th marriage window", }, { "date": "2026-08-19", "domain": "marriage", "score": 2, "confidence": "medium", "event_count": 1, "signal_families": ["relationship_pressure"], "event_ids": ["SaturnAspect7thRelationshipWindow"], "top_signal_label": "Saturn aspecting 7th relationship window", }, ], "top_daily_window": { "date": "2026-08-02", "domain": "marriage", "score": 5, "confidence": "high", "event_count": 2, "signal_families": ["marriage_trigger"], "event_ids": ["GocharJupiterIn7th", "JupiterSupportsMarriageAxis"], "top_signal_label": "Jupiter in 7th marriage window", }, "source_metadata": {}, } strict = _collect_strict_evidence("relationship", result) summary = strict["monthly_adjudication_summary"] assert summary["route"] == "relationship" assert summary["primary_state"]["value"] in {"推进", "启动", "筛选", "收束", "观察"} assert summary["manifestation_mode"]["value"] assert summary["friction_source"]["value"] assert summary["time_confidence"]["value"] in {"day_supported", "month_supported", "month_only", "blocked"} assert isinstance(summary["supporting_days"], list) assert summary["supporting_days"][0]["date"] == "2026-08-02" def test_relationship_summary_modifier_frame_surfaces_label_lift_related_modifiers() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) modifier = strict["multi_reference_reading_summary"]["modifier_frame"] assert "functional_benefic_malefic" in modifier assert modifier["manifestation_split"]["role"] == "modifier_only" assert "legal_marriage" in modifier["manifestation_split"]["signals"] def test_relationship_multi_reference_summary_carries_audit_gate_frame() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) frame = strict["multi_reference_reading_summary"]["audit_gate_frame"] assert frame["functional_benefic_malefic"]["used"] in {True, False} assert frame["source_priority_boundary"]["blocked_items"] == strict["blocked_items"] def test_relationship_synastry_bridge_adds_context_and_small_score_bump_without_overriding_label_gate() -> None: base = _collect_strict_evidence("relationship", _base_relationship_result()) result = _base_relationship_result() result["modules"]["synastry"] = { "total_score": 29.0, "max_score": 36.0, "is_approved": True, "additional_kutas": { "Mahendra": "good", "StreeDeergha": "good", "Vedha": "good", "Rajju": {"result": "good", "group": None, "effect": ""}, "BadConstellations": "good", }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["synastry_relationship_support"] == { "level": "supportive", "source": "synastry_relationship_bridge_v1", "signals": [ "ashtakoot_approved", "ashtakoot_high_score", "mahendra_support", "stree_deergha_support", "vedha_clean", "rajju_clean", "bad_constellations_clean", "kuta_exception_clean", ], "total_score": 29.0, "approved": True, } assert strict["event_judgement"]["dominant_label"] == "legal_marriage" assert strict["event_judgement"]["score"] == base["event_judgement"]["score"] + 5 assert "synastry_support" in strict["event_judgement"]["secondary_context"] def test_relationship_synastry_bridge_cannot_lift_label_when_core_marriage_layers_are_missing() -> None: result = _base_relationship_result() del result["modules"]["varga_full"]["D9_Navamsa"] del result["modules"]["special_lagnas"]["Upapada_Lagna"] result["modules"]["synastry"] = { "total_score": 31.0, "is_approved": True, "additional_kutas": { "Vedha": "good", "Rajju": {"result": "good"}, "BadConstellations": "good", }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["synastry_relationship_support"]["level"] == "supportive" assert strict["event_judgement"]["dominant_label"] is None assert "synastry_support" in strict["event_judgement"]["secondary_context"] assert "d9_navamsa" in strict["missing_evidence"] assert "upapada_lagna" in strict["missing_evidence"] def test_relationship_synastry_bridge_understands_nested_bad_constellations_and_exception_mitigation() -> None: result = _base_relationship_result() result["modules"]["synastry"] = { "total_score": 18.0, "max_score": 36.0, "is_approved": True, "exceptions": ["Nadi Dosha mitigated by good Bhakoot and Rajju."], "additional_kutas": { "Mahendra": "good", "StreeDeergha": "good", "Vedha": "good", "Rajju": {"result": "good", "group": None, "effect": ""}, "BadConstellations": {"result": "good", "issues": []}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["synastry_relationship_support"] == { "level": "moderate", "source": "synastry_relationship_bridge_v1", "signals": [ "ashtakoot_approved", "exception_mitigated_match", "mahendra_support", "stree_deergha_support", "vedha_clean", "rajju_clean", "bad_constellations_clean", "kuta_exception_clean", ], "total_score": 18.0, "approved": True, } assert "synastry_support" in strict["event_judgement"]["secondary_context"] assert "synastry_compatibility_support" in strict["event_judgement"]["secondary_context"] assert "synastry_protective_kuta_support" in strict["event_judgement"]["secondary_context"] assert "synastry_exception_mitigated" in strict["event_judgement"]["secondary_context"] assert "synastry_kuta_exception_clean" in strict["event_judgement"]["secondary_context"] assert "mahendra_support" in strict["event_judgement"]["secondary_context"] assert "stree_deergha_support" in strict["event_judgement"]["secondary_context"] assert "vedha_clean" in strict["event_judgement"]["secondary_context"] assert "rajju_clean" in strict["event_judgement"]["secondary_context"] assert "bad_constellations_clean" in strict["event_judgement"]["secondary_context"] def test_relationship_synastry_exception_alone_cannot_create_false_lift_or_clean_kuta_context() -> None: result = _base_relationship_result() del result["modules"]["varga_full"]["D9_Navamsa"] del result["modules"]["special_lagnas"]["Upapada_Lagna"] del result["modules"]["vivah_saham"] del result["modules"]["narayana_dasha"] result["modules"]["synastry"] = { "total_score": 14.0, "max_score": 36.0, "is_approved": False, "exceptions": ["Nadi exception discussed but not fully mitigated."], "additional_kutas": { "Mahendra": "good", "StreeDeergha": "good", "Vedha": "bad", "Rajju": {"result": "bad", "group": "same", "effect": "dosha"}, "BadConstellations": {"result": "bad", "issues": ["vedha clash"]}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["synastry_relationship_support"] == { "level": "none", "source": "synastry_relationship_bridge_v1", "signals": [ "exception_mitigated_match", "mahendra_support", "stree_deergha_support", ], "total_score": 14.0, "approved": False, } assert strict["event_judgement"]["dominant_label"] is None assert "synastry_support" not in strict["event_judgement"]["secondary_context"] assert "synastry_compatibility_support" not in strict["event_judgement"]["secondary_context"] assert "synastry_protective_kuta_support" not in strict["event_judgement"]["secondary_context"] assert "synastry_exception_mitigated" not in strict["event_judgement"]["secondary_context"] assert "synastry_kuta_exception_clean" not in strict["event_judgement"]["secondary_context"] assert "mahendra_support" not in strict["event_judgement"]["secondary_context"] assert "rajju_clean" not in strict["event_judgement"]["secondary_context"] def test_relationship_good_kutas_cannot_override_weak_core_marriage_promise() -> None: result = _base_relationship_result() del result["modules"]["vivah_saham"] del result["modules"]["dasa_convergence"] result["modules"]["jaimini"] = { "darakaraka": {"planet": "Venus", "house": 3}, } result["modules"]["synastry"] = { "total_score": 31.0, "max_score": 36.0, "is_approved": True, "additional_kutas": { "Mahendra": "good", "StreeDeergha": "good", "Vedha": "good", "Rajju": {"result": "good", "group": None, "effect": ""}, "BadConstellations": {"result": "good", "issues": []}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["jaimini_marriage_support"] == { "level": "weak", "signals": ["darakaraka_active"], "source": "jaimini_bridge_v1", } assert strict["present_evidence"]["synastry_relationship_support"]["level"] == "supportive" assert strict["event_judgement"]["dominant_label"] is None assert "synastry_support" in strict["event_judgement"]["secondary_context"] assert "synastry_compatibility_support" in strict["event_judgement"]["secondary_context"] assert "synastry_protective_kuta_support" in strict["event_judgement"]["secondary_context"] assert "jaimini_support" not in strict["event_judgement"]["secondary_context"] def test_relationship_protective_kutas_do_not_block_confidence_downgrade_when_timing_layers_conflict() -> None: result = _base_relationship_result() result["modules"]["synastry"] = { "total_score": 31.0, "max_score": 36.0, "is_approved": True, "additional_kutas": { "Mahendra": "good", "StreeDeergha": "good", "Vedha": "good", "Rajju": {"result": "good", "group": None, "effect": ""}, "BadConstellations": {"result": "good", "issues": []}, }, } result["modules"]["external_activation"] = { "evidence_ledger": [ { "source": "vedastro_service_adapter_candidate", "operation": "range_scan", "domain": "career", "event_id": "career_window_only", "signal_key": "career_only_external_window", "score": 74, } ] } result["modules"]["dasa_convergence"] = { "domain_activations": { "career_status": {"convergence_level": "L4", "probability": "70-85%"} } } strict = _collect_strict_evidence("relationship", result) assert "synastry_protective_kuta_support" in strict["event_judgement"]["secondary_context"] assert strict["event_judgement"]["dominant_label"] == "legal_marriage" assert strict["confidence_cap"] == "low" def test_relationship_narrative_payload_turns_synastry_taxonomy_into_user_readable_boundary() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) strict["present_evidence"]["synastry_relationship_support"] = { "level": "supportive", "source": "synastry_relationship_bridge_v1", "signals": [ "ashtakoot_approved", "mahendra_support", "stree_deergha_support", "vedha_clean", "rajju_clean", ], "total_score": 29.0, "approved": True, } strict["event_judgement"]["secondary_context"] = strict["event_judgement"]["secondary_context"] + [ "synastry_support", "synastry_compatibility_support", "synastry_protective_kuta_support", ] strict["confidence_cap"] = "low" payload = jyotish_engine._build_relationship_narrative_payload(strict) assert payload["monthly_frame"]["primary_state"]["value"] assert payload["monthly_frame"]["manifestation_mode"]["value"] assert payload["monthly_frame"]["friction_source"]["value"] assert payload["monthly_frame"]["time_confidence"]["value"] assert "婚恋" in payload["headline"] assert any("合盘" in item for item in payload["strengths"]) assert any("protective kuta" in item.lower() for item in payload["strengths"]) assert any("dual dasha" in item.lower() for item in payload["risks"]) assert any("legal_marriage" in item for item in payload["boundaries"]) assert "月度主状态" in payload["markdown"] assert "落地形式" in payload["markdown"] assert "阻力来源" in payload["markdown"] assert "时间置信度" in payload["markdown"] assert "D9" in payload["markdown"] assert "dual dasha" in payload["markdown"] def test_relationship_narrative_payload_exposes_public_formalization_candidate_as_context_only() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) strict["event_judgement"]["dominant_label"] = None strict["event_judgement"]["secondary_context"] = [ "darakaraka_active", "jaimini_support", "ul_support", "external_activation_support", "public_formalization_candidate", ] strict["present_evidence"].pop("vivah_saham", None) strict["present_evidence"].pop("marriage_convergence", None) strict["confidence_cap"] = "low" payload = jyotish_engine._build_relationship_narrative_payload(strict) assert any("public_formalization" in item for item in payload["strengths"]) assert any("不等于法律婚姻" in item for item in payload["boundaries"]) assert "public_formalization_candidate" in payload["markdown"] assert "legal_marriage" in payload["markdown"] def test_relationship_narrative_payload_for_public_formalization_candidate_with_timing_conflict_explicitly_warns_not_to_misread_as_near_marriage() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) strict["event_judgement"]["dominant_label"] = None strict["event_judgement"]["secondary_context"] = [ "darakaraka_active", "jaimini_support", "ul_support", "external_activation_support", "public_formalization_candidate", ] strict["present_evidence"].pop("vivah_saham", None) strict["present_evidence"].pop("marriage_convergence", None) strict["confidence_cap"] = "low" payload = jyotish_engine._build_relationship_narrative_payload(strict) assert any("public_formalization_candidate" in item for item in payload["strengths"]) assert any("不能误读成接近结婚" in item for item in payload["risks"]) assert any("不等于法律婚姻" in item for item in payload["boundaries"]) assert "不能误读成接近结婚" in payload["markdown"] def test_relationship_narrative_payload_does_not_translate_public_formalization_candidate_plus_synastry_support_plus_weak_core_promise_into_marriage_approach() -> None: strict = _collect_strict_evidence("relationship", _base_relationship_result()) strict["event_judgement"]["dominant_label"] = None strict["event_judgement"]["secondary_context"] = [ "darakaraka_active", "ul_support", "synastry_support", "synastry_compatibility_support", "public_formalization_candidate", ] strict["present_evidence"]["jaimini_marriage_support"] = { "level": "weak", "signals": ["darakaraka_active"], "source": "jaimini_bridge_v1", } strict["present_evidence"].pop("vivah_saham", None) strict["present_evidence"].pop("marriage_convergence", None) strict["confidence_cap"] = "low" payload = jyotish_engine._build_relationship_narrative_payload(strict) assert any("合盘支持已进入婚恋主链" in item for item in payload["strengths"]) assert any("公开化/关系可见度候选" in item for item in payload["strengths"]) assert any("不能误读成接近结婚" in item for item in payload["risks"]) assert any("不得越权抬升 legal_marriage" in item for item in payload["boundaries"]) assert "public_formalization_candidate" in payload["markdown"] assert "legal_marriage" in payload["markdown"] def test_relationship_dignity_guardrail_ignores_non_relevant_planets() -> None: result = _base_relationship_result() result["modules"]["chart"] = { "ascendant": {"sign": "Leo"}, "planets": { "Mars": {"status": "落陷取消(Neecha Bhanga)"}, "Mercury": {"status": "极敌(Great Enemy)"}, "Venus": {"status": "中性(Neutral)"}, "Jupiter": {"status": "中性(Neutral)"}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["dignity_guardrail"]["score_delta"] == 0 assert strict["event_judgement"]["dominant_label"] == "legal_marriage" assert "dignity_supportive_recovery" not in strict["event_judgement"]["secondary_context"] assert "dignity_high_friction" not in strict["event_judgement"]["secondary_context"] def test_relationship_dignity_guardrail_uses_relevant_planets_only() -> None: result = _base_relationship_result() result["modules"]["chart"] = { "ascendant": {"sign": "Aries"}, "planets": { "Venus": {"status": "落陷取消(Neecha Bhanga)"}, "Jupiter": {"status": "中性(Neutral)"}, "Saturn": {"status": "极敌(Great Enemy)"}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["dignity_guardrail"]["score_delta"] == 5 assert "dignity_supportive_recovery" in strict["event_judgement"]["secondary_context"] def test_relationship_dignity_guardrail_treats_great_friend_on_relevant_planet_as_supportive_context() -> None: result = _base_relationship_result() result["modules"]["chart"] = { "ascendant": {"sign": "Leo"}, "planets": { "Venus": {"status": "极友(Great Friend)"}, "Jupiter": {"status": "中性(Neutral)"}, "Saturn": {"status": "中性(Neutral)"}, }, } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["dignity_guardrail"]["status"] == "caution" assert strict["present_evidence"]["dignity_guardrail"]["score_delta"] == 3 assert "dignity_supportive_friendship" in strict["event_judgement"]["secondary_context"] def test_relationship_caps_confidence_when_provided_shadbala_components_are_incomplete() -> None: result = _base_relationship_result() result["modules"]["dasa_convergence"]["domain_activations"]["marriage_partnership"] = { "convergence_level": "L4", "probability": "70-85%", } result["modules"]["shadbala"] = {"planets": {"Venus": {"total_rupa": 8.2}}} strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["shadbala_component_audit"] == { "status": "incomplete", "source": "shadbala.planets", "required_components": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"], "missing": {"Venus": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]}, } assert strict["confidence_cap"] == "low" assert "shadbala_component_gap" in strict["event_judgement"]["secondary_context"] def test_relationship_accepts_complete_shadbala_components_without_cap_penalty() -> None: result = _base_relationship_result() result["modules"]["dasa_convergence"]["domain_activations"]["marriage_partnership"] = { "convergence_level": "L4", "probability": "70-85%", } result["modules"]["shadbala"] = { "planets": { "Venus": { "components": { "sthana": 1.0, "dig": 0.8, "kala": 1.2, "chesta": 0.7, "naisargika": 0.5, "drik": 0.3, }, "total_rupa": 4.5, } } } strict = _collect_strict_evidence("relationship", result) assert strict["present_evidence"]["shadbala_component_audit"]["status"] == "complete" assert strict["confidence_cap"] == "medium-high" assert "shadbala_component_gap" not in strict["event_judgement"]["secondary_context"]