diff --git a/docs/research/argala_virodhargala_adjudicator_bridge_2026_06_28.md b/docs/research/argala_virodhargala_adjudicator_bridge_2026_06_28.md new file mode 100644 index 00000000..b4d40c21 --- /dev/null +++ b/docs/research/argala_virodhargala_adjudicator_bridge_2026_06_28.md @@ -0,0 +1,54 @@ +# Argala / Virodhargala Adjudicator Bridge + +Date: 2026-06-28 + +## Scope + +This pass connects an already computed Jyotish technique into strict adjudication: + +- Career route reads `modules.argala.houses.house_10`. +- Relationship route reads `modules.argala.houses.house_7`. +- The bridge never recalculates Argala and never overrides the native `scripts/argala.py` result. + +## Contract + +`present_evidence.argala_support` is a secondary modifier: + +```json +{ + "level": "none | supportive | obstructive", + "target_house": 10, + "source": "argala_house_bridge_v1", + "signals": ["argala_support"], + "raw": { + "net_result": "supported", + "argala_count": 2, + "virodhargala_count": 0 + } +} +``` + +## Scoring Boundary + +- `supportive`: `score +5`, `secondary_context += ["argala_support"]` +- `obstructive`: `score -5`, `secondary_context += ["virodhargala_obstruction"]` +- `none`: no score effect + +Argala is not a hard gate. Missing Argala must not block the route. + +## Anti-Overclaim Rule + +Argala / Virodhargala cannot directly set: + +- `dominant_label` +- `payout_label` +- route `verdict` + +It can only nudge the score through the normal threshold system. This preserves the project's rule that no single technique may override D1/Varga/Dasha/Transit convergence. + +## Regression Targets + +- Career: supportive 10th-house Argala is collected and appears as `argala_support`. +- Relationship: obstructive 7th-house Virodhargala is collected and appears as `virodhargala_obstruction`. +- Existing Jaimini, VedAstro, dignity, finance, and native Argala tests remain green. + diff --git a/mcp_server.py b/mcp_server.py index 164e44ef..549a99a7 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -448,6 +448,41 @@ def _derive_external_activation_support(modules: Dict[str, Any], domain: str) -> } +def _derive_argala_support(modules: Dict[str, Any], target_house: int) -> Dict[str, Any]: + house_data = _safe_get(modules, "argala", "houses", f"house_{target_house}") + if not isinstance(house_data, dict): + return { + "level": "none", + "target_house": target_house, + "source": "argala_house_bridge_v1", + "signals": [], + "raw": None, + } + + net_result = house_data.get("net_result") + if net_result == "supported": + level = "supportive" + signals = ["argala_support"] + elif net_result == "obstructed": + level = "obstructive" + signals = ["virodhargala_obstruction"] + else: + level = "none" + signals = [] + + return { + "level": level, + "target_house": target_house, + "source": "argala_house_bridge_v1", + "signals": signals, + "raw": { + "net_result": net_result, + "argala_count": house_data.get("argala_count"), + "virodhargala_count": house_data.get("virodhargala_count"), + }, + } + + def _sign_to_index(sign: str) -> Optional[int]: try: return _SIGNS.index(sign) @@ -583,6 +618,11 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s score += 10 if present.get("vimshottari_current") else 0 score += 10 if present.get("narayana_current") else 0 score += _convergence_score(present.get("career_convergence")) + argala_support = present.get("argala_support") or {} + if argala_support.get("level") == "supportive": + score += 5 + elif argala_support.get("level") == "obstructive": + score -= 5 external_activation = present.get("external_activation") or {} if external_activation.get("level") == "moderate": score += 5 @@ -608,6 +648,10 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s secondary_context.append("amk_active") if present.get("karakamsha"): secondary_context.append("karakamsha_context") + if argala_support.get("level") == "supportive": + secondary_context.append("argala_support") + elif argala_support.get("level") == "obstructive": + secondary_context.append("virodhargala_obstruction") if external_activation.get("level") == "moderate": secondary_context.append("external_activation_support") @@ -637,6 +681,7 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s "a10_karma_pada", "amatyakaraka", "karakamsha", + "argala_support", ) if present.get(key) ], @@ -653,6 +698,11 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s score += _convergence_score(present.get("marriage_convergence")) dignity_guardrail = present.get("dignity_guardrail") or {} score += dignity_guardrail.get("score_delta", 0) + argala_support = present.get("argala_support") or {} + if argala_support.get("level") == "supportive": + score += 5 + elif argala_support.get("level") == "obstructive": + score -= 5 if missing: score = min(score, 35) score = min(score, 100) @@ -674,6 +724,10 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s secondary_context.append("jaimini_support") if present.get("upapada_lagna"): secondary_context.append("ul_support") + if argala_support.get("level") == "supportive": + secondary_context.append("argala_support") + elif argala_support.get("level") == "obstructive": + secondary_context.append("virodhargala_obstruction") external_activation = present.get("external_activation") or {} if external_activation.get("level") == "moderate": secondary_context.append("external_activation_support") @@ -713,6 +767,7 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s "narayana_current", "darakaraka", "upapada_lagna", + "argala_support", ) if present.get(key) ], @@ -843,9 +898,10 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An "narayana_current": _safe_get(modules, "narayana_dasha", "current_dasha"), "career_convergence": domain_activations.get("career_status"), } + present["argala_support"] = _derive_argala_support(modules, 10) present["external_activation"] = _derive_external_activation_support(modules, "career") missing = [key for key, value in present.items() if key not in { - "external_activation" + "external_activation", "argala_support" } and value in (None, {}, [], "")] convergence = present["career_convergence"] or {} confidence_cap = "medium" @@ -894,11 +950,12 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An } present["jaimini_timing_support"] = _safe_get(modules, "jaimini", "marriage_timing_support") present["jaimini_marriage_support"] = _derive_jaimini_marriage_support(present) + present["argala_support"] = _derive_argala_support(modules, 7) present["external_activation"] = _derive_external_activation_support(modules, "marriage") present["dignity_guardrail"] = _derive_dignity_guardrail(route, present) missing = [ key for key, value in present.items() - if key not in {"chart", "external_activation", "dignity_guardrail", "jaimini_marriage_support", "jaimini_timing_support"} + if key not in {"chart", "external_activation", "dignity_guardrail", "jaimini_marriage_support", "jaimini_timing_support", "argala_support"} and value in (None, {}, [], "") ] convergence = present["marriage_convergence"] or {} diff --git a/tests/test_mcp_strict_workflow_career.py b/tests/test_mcp_strict_workflow_career.py index 91163600..2cf3497c 100644 --- a/tests/test_mcp_strict_workflow_career.py +++ b/tests/test_mcp_strict_workflow_career.py @@ -25,6 +25,15 @@ def _base_career_result() -> dict: "career_status": {"convergence_level": "L2", "probability": "35-50%"} } }, + "argala": { + "houses": { + "house_10": { + "net_result": "supported", + "argala_count": 2, + "virodhargala_count": 0, + } + } + }, } } @@ -46,6 +55,7 @@ def test_career_collects_a10_amk_karakamsha_as_strict_evidence() -> None: "a10_active", "amk_active", "karakamsha_context", + "argala_support", ] @@ -62,4 +72,25 @@ def test_career_blocks_label_when_d10_is_missing_but_preserves_jaimini_context() "a10_active", "amk_active", "karakamsha_context", + "argala_support", ] + + +def test_career_argala_bridge_uses_tenth_house_as_modifier_only() -> None: + result = _base_career_result() + + strict = _collect_strict_evidence("career", result) + + assert strict["present_evidence"]["argala_support"] == { + "level": "supportive", + "target_house": 10, + "source": "argala_house_bridge_v1", + "signals": ["argala_support"], + "raw": { + "net_result": "supported", + "argala_count": 2, + "virodhargala_count": 0, + }, + } + assert strict["event_judgement"]["dominant_label"] == "career_status" + assert "argala_support" in strict["event_judgement"]["secondary_context"] diff --git a/tests/test_mcp_strict_workflow_relationship.py b/tests/test_mcp_strict_workflow_relationship.py index abf7996c..9fdb623a 100644 --- a/tests/test_mcp_strict_workflow_relationship.py +++ b/tests/test_mcp_strict_workflow_relationship.py @@ -23,6 +23,15 @@ def _base_relationship_result() -> dict: "marriage_partnership": {"convergence_level": "L1", "probability": "+15-20%"} } }, + "argala": { + "houses": { + "house_7": { + "net_result": "obstructed", + "argala_count": 1, + "virodhargala_count": 2, + } + } + }, } } @@ -40,6 +49,7 @@ def test_relationship_jaimini_bridge_lifts_legal_marriage_label() -> None: "darakaraka_active", "jaimini_support", "ul_support", + "virodhargala_obstruction", ] @@ -59,9 +69,30 @@ def test_relationship_jaimini_bridge_stays_context_only_when_d9_missing() -> Non "darakaraka_active", "jaimini_support", "ul_support", + "virodhargala_obstruction", ] +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"] = {