From 4a46b5faad8269b3320edfc4c8e9a57c5ca1a262 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 28 Jun 2026 12:10:07 +0800 Subject: [PATCH] Add Jaimini marriage label bridge --- ...ator_jaimini_bridge_v1_audit_2026_06_28.md | 68 +++++++++++++++++ mcp_server.py | 74 ++++++++++++++++++- .../test_mcp_strict_workflow_relationship.py | 62 ++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 docs/research/marriage_adjudicator_jaimini_bridge_v1_audit_2026_06_28.md create mode 100644 tests/test_mcp_strict_workflow_relationship.py diff --git a/docs/research/marriage_adjudicator_jaimini_bridge_v1_audit_2026_06_28.md b/docs/research/marriage_adjudicator_jaimini_bridge_v1_audit_2026_06_28.md new file mode 100644 index 00000000..12461ff9 --- /dev/null +++ b/docs/research/marriage_adjudicator_jaimini_bridge_v1_audit_2026_06_28.md @@ -0,0 +1,68 @@ +# Marriage Adjudicator Jaimini Bridge v1 Audit (2026-06-28) + +## Scope + +This pass adds the smallest Jaimini bridge to the relationship strict workflow. + +It fixes only `label-lift failure` for legal-marriage cases. + +## Implemented Behavior + +The relationship event judgement now preserves legacy fields: + +- `score` +- `verdict` +- `primary_drivers` + +and adds: + +- `dominant_label` +- `secondary_context` + +## Boundary + +v1 may lift only: + +- `legal_marriage` + +v1 does not lift: + +- `relationship_formation` +- `public_formalization` + +## Jaimini Bridge + +`present_evidence.jaimini_marriage_support` has: + +- `level`: `none`, `weak`, or `moderate` +- `signals` +- `source`: `jaimini_bridge_v1` + +`strong` is intentionally not available in v1. + +## Hard Gates + +`dominant_label = legal_marriage` requires: + +- D9 present +- UL present +- Vimshottari present +- Narayana present +- Vivah Saham or marriage convergence present +- moderate Jaimini support + +If a hard gate is missing, Jaimini evidence remains context-only. + +## Verification + +Commands: + +```bash +python3 -m pytest tests/test_mcp_strict_workflow_relationship.py -q +python3 -m pytest tests/test_mcp_strict_workflow_finance.py -q +``` + +Observed: + +- relationship: `2 passed` +- finance: `16 passed` diff --git a/mcp_server.py b/mcp_server.py index 0eb5b4d4..002e6ff3 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -372,6 +372,45 @@ def _check_external_avayogi_risk(result: Dict[str, Any]) -> Optional[Dict[str, A } +def _derive_jaimini_marriage_support(present: Dict[str, Any]) -> Dict[str, Any]: + signals: List[str] = [] + darakaraka = present.get("darakaraka") + upapada_lagna = present.get("upapada_lagna") + + if darakaraka: + signals.append("darakaraka_active") + + if isinstance(darakaraka, dict): + if darakaraka.get("house") == 7 or darakaraka.get("house_from_lagna") == 7: + signals.append("dk_7h_link") + if darakaraka.get("ul_link") or darakaraka.get("linked_to_ul"): + signals.append("dk_ul_link") + + if isinstance(upapada_lagna, dict) and isinstance(darakaraka, dict): + dk_sign = darakaraka.get("sign") + ul_sign = upapada_lagna.get("sign") + if dk_sign and ul_sign and dk_sign == ul_sign and "dk_ul_link" not in signals: + signals.append("dk_ul_link") + + if present.get("jaimini_timing_support"): + signals.append("jaimini_dasha_support") + + if not signals: + level = "none" + elif len(signals) == 1: + level = "weak" + elif "darakaraka_active" in signals: + level = "moderate" + else: + level = "weak" + + return { + "level": level, + "signals": signals, + "source": "jaimini_bridge_v1", + } + + def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[str]) -> Dict[str, Any]: if route == "relationship": score = 0 @@ -395,10 +434,37 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s verdict = "weak_window_needs_confirmation" else: verdict = "insufficient_evidence" + jaimini_support = present.get("jaimini_marriage_support") or {} + secondary_context: List[str] = [] + if present.get("darakaraka"): + secondary_context.append("darakaraka_active") + if jaimini_support.get("level") == "moderate": + secondary_context.append("jaimini_support") + if present.get("upapada_lagna"): + secondary_context.append("ul_support") + + hard_gate_missing = any( + key in missing for key in ( + "d9_navamsa", + "upapada_lagna", + "vimshottari_current", + "narayana_current", + ) + ) + label_support_present = bool(present.get("vivah_saham") or present.get("marriage_convergence")) + dominant_label = None + if ( + not hard_gate_missing + and label_support_present + and jaimini_support.get("level") == "moderate" + ): + dominant_label = "legal_marriage" return { "event_family": "relationship", "score": score, "verdict": verdict, + "dominant_label": dominant_label, + "secondary_context": secondary_context, "primary_drivers": [ key for key in ( "marriage_convergence", @@ -525,7 +591,13 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An "narayana_current": _safe_get(modules, "narayana_dasha", "current_dasha"), "marriage_convergence": domain_activations.get("marriage_partnership"), } - missing = [key for key, value in present.items() if value in (None, {}, [], "")] + present["jaimini_timing_support"] = _safe_get(modules, "jaimini", "marriage_timing_support") + present["jaimini_marriage_support"] = _derive_jaimini_marriage_support(present) + missing = [ + key for key, value in present.items() + if key not in {"jaimini_marriage_support", "jaimini_timing_support"} + and value in (None, {}, [], "") + ] convergence = present["marriage_convergence"] or {} confidence_cap = "medium" if missing: diff --git a/tests/test_mcp_strict_workflow_relationship.py b/tests/test_mcp_strict_workflow_relationship.py new file mode 100644 index 00000000..fc1cd763 --- /dev/null +++ b/tests/test_mcp_strict_workflow_relationship.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Regression tests for MCP relationship event adjudication.""" + +from __future__ import annotations + +from mcp_server import _collect_strict_evidence + + +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%"} + } + }, + } + } + + +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 strict["event_judgement"]["secondary_context"] == [ + "darakaraka_active", + "jaimini_support", + "ul_support", + ] + + +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 strict["event_judgement"]["secondary_context"] == [ + "darakaraka_active", + "jaimini_support", + "ul_support", + ]