Bridge synastry support and enrich VedAstro range scan
This commit is contained in:
@@ -12,6 +12,7 @@ This file is the small index for the current engineering fronts that still drive
|
||||
## Relationship Adjudication
|
||||
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/research/marriage_adjudicator_first_pass_audit_2026_06_27.md`
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/research/isolated_asset_bridge_audit_2026_06_28.md`
|
||||
- `/Users/wuyongnaren/Documents/印度占星/references/event_judgment_marriage.md`
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/superpowers/specs/2026-06-28-jaimini-marriage-bridge-v1-design.md`
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Isolated Asset Bridge Audit - 2026-06-28
|
||||
|
||||
## Scope
|
||||
|
||||
This audit records the first small bridge pass after the VedAstro parity matrix identified high-value local assets that were present but not fully exploited by strict adjudicators.
|
||||
|
||||
## Bridges
|
||||
|
||||
### Synastry / Ashtakoot -> relationship strict evidence
|
||||
|
||||
- New evidence key: `synastry_relationship_support`
|
||||
- Source: `synastry_relationship_bridge_v1`
|
||||
- Inputs accepted:
|
||||
- `modules.synastry.total_score`
|
||||
- `modules.synastry.is_approved` or `is_match_approved`
|
||||
- selected clean additional Kuta signals: `Vedha`, `Rajju`, `BadConstellations`
|
||||
- Output levels:
|
||||
- `none`
|
||||
- `moderate`
|
||||
- `supportive`
|
||||
|
||||
Boundary:
|
||||
|
||||
- Adds at most `+5` to relationship score.
|
||||
- Adds `synastry_support` to `secondary_context`.
|
||||
- Does not lift `dominant_label`.
|
||||
- Does not bypass D9, UL, Vimshottari, Narayana, Vivah Saham, or marriage convergence gates.
|
||||
|
||||
### Kakshya -> career strict evidence
|
||||
|
||||
- New evidence key: `kakshya_career_support`
|
||||
- Source: `kakshya_career_bridge_v1`
|
||||
- Inputs accepted:
|
||||
- `modules.kakshya.summary.average_strength`
|
||||
- Output levels:
|
||||
- `none`
|
||||
- `supportive`
|
||||
- `obstructive`
|
||||
|
||||
Boundary:
|
||||
|
||||
- Adds `+2` for supportive career Kakshya.
|
||||
- Subtracts `-2` for obstructive career Kakshya.
|
||||
- Adds a secondary context flag.
|
||||
- Does not lift `dominant_label` without the existing career hard gates.
|
||||
|
||||
### VedAstro range scan adapter summary
|
||||
|
||||
- Adds `event_count`.
|
||||
- Adds `top_event`.
|
||||
- Keeps the complete `evidence_ledger` as the source of truth.
|
||||
|
||||
Boundary:
|
||||
|
||||
- The adapter summary is convenience metadata only.
|
||||
- External range scan evidence remains `oracle_only` until promoted by adjudicator tests.
|
||||
|
||||
## Regression Coverage
|
||||
|
||||
- `tests/test_mcp_strict_workflow_relationship.py`
|
||||
- `tests/test_mcp_strict_workflow_career.py`
|
||||
- `tests/test_vedastro_service_adapter_executor.py`
|
||||
|
||||
## Next Follow-Up
|
||||
|
||||
Build a full relationship bridge around Synastry/Ashtakoot only after real couple benchmark cases are added. This pass intentionally keeps matching evidence secondary.
|
||||
+92
-2
@@ -449,6 +449,59 @@ def _derive_external_activation_support(modules: Dict[str, Any], domain: str) ->
|
||||
}
|
||||
|
||||
|
||||
def _derive_synastry_relationship_support(modules: Dict[str, Any]) -> Dict[str, Any]:
|
||||
synastry = _safe_get(modules, "synastry")
|
||||
base = {
|
||||
"level": "none",
|
||||
"source": "synastry_relationship_bridge_v1",
|
||||
"signals": [],
|
||||
"total_score": None,
|
||||
"approved": False,
|
||||
}
|
||||
if not isinstance(synastry, dict):
|
||||
return base
|
||||
|
||||
try:
|
||||
total_score = float(synastry.get("total_score"))
|
||||
except (TypeError, ValueError):
|
||||
total_score = None
|
||||
|
||||
approved = bool(
|
||||
synastry.get("is_approved")
|
||||
or synastry.get("is_match_approved")
|
||||
)
|
||||
signals: List[str] = []
|
||||
if approved:
|
||||
signals.append("ashtakoot_approved")
|
||||
if total_score is not None and total_score >= 27:
|
||||
signals.append("ashtakoot_high_score")
|
||||
additional_kutas = synastry.get("additional_kutas")
|
||||
if isinstance(additional_kutas, dict):
|
||||
vedha_good = additional_kutas.get("Vedha") == "good"
|
||||
bad_constellations_good = additional_kutas.get("BadConstellations") == "good"
|
||||
rajju = additional_kutas.get("Rajju")
|
||||
rajju_good = isinstance(rajju, dict) and rajju.get("result") == "good"
|
||||
if vedha_good and bad_constellations_good and rajju_good:
|
||||
signals.append("kuta_exception_clean")
|
||||
|
||||
if approved and total_score is not None and total_score >= 27:
|
||||
level = "supportive"
|
||||
elif approved or (total_score is not None and total_score >= 24):
|
||||
level = "moderate"
|
||||
else:
|
||||
level = "none"
|
||||
|
||||
base.update(
|
||||
{
|
||||
"level": level,
|
||||
"signals": signals,
|
||||
"total_score": total_score,
|
||||
"approved": approved,
|
||||
}
|
||||
)
|
||||
return base
|
||||
|
||||
|
||||
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):
|
||||
@@ -656,6 +709,26 @@ def _derive_kakshya_finance_support(kakshya: Any) -> Dict[str, Any]:
|
||||
return base
|
||||
|
||||
|
||||
def _derive_kakshya_career_support(kakshya: Any) -> Dict[str, Any]:
|
||||
base = {
|
||||
"level": "none",
|
||||
"source": "kakshya_career_bridge_v1",
|
||||
"signals": [],
|
||||
"average_strength": None,
|
||||
}
|
||||
avg = _safe_get(kakshya, "summary", "average_strength")
|
||||
if not isinstance(avg, (int, float)):
|
||||
return base
|
||||
base["average_strength"] = float(avg)
|
||||
if avg >= 6.5:
|
||||
base["level"] = "supportive"
|
||||
base["signals"] = ["kakshya_career_support"]
|
||||
elif avg <= 4.5:
|
||||
base["level"] = "obstructive"
|
||||
base["signals"] = ["kakshya_career_friction"]
|
||||
return base
|
||||
|
||||
|
||||
def _sign_to_index(sign: str) -> Optional[int]:
|
||||
try:
|
||||
return _SIGNS.index(sign)
|
||||
@@ -791,6 +864,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"))
|
||||
kakshya_career_support = present.get("kakshya_career_support") or {}
|
||||
if kakshya_career_support.get("level") == "supportive":
|
||||
score += 2
|
||||
elif kakshya_career_support.get("level") == "obstructive":
|
||||
score -= 2
|
||||
argala_support = present.get("argala_support") or {}
|
||||
if argala_support.get("level") == "supportive":
|
||||
score += 5
|
||||
@@ -824,6 +902,10 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
shadbala_component_audit = present.get("shadbala_component_audit") or {}
|
||||
if shadbala_component_audit.get("status") in {"blocked", "incomplete"}:
|
||||
secondary_context.append("shadbala_component_gap")
|
||||
if kakshya_career_support.get("level") == "supportive":
|
||||
secondary_context.append("kakshya_career_support")
|
||||
elif kakshya_career_support.get("level") == "obstructive":
|
||||
secondary_context.append("kakshya_career_friction")
|
||||
if argala_support.get("level") == "supportive":
|
||||
secondary_context.append("argala_support")
|
||||
elif argala_support.get("level") == "obstructive":
|
||||
@@ -874,6 +956,9 @@ 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)
|
||||
synastry_support = present.get("synastry_relationship_support") or {}
|
||||
if synastry_support.get("level") == "supportive":
|
||||
score += 5
|
||||
argala_support = present.get("argala_support") or {}
|
||||
if argala_support.get("level") == "supportive":
|
||||
score += 5
|
||||
@@ -900,6 +985,8 @@ 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 synastry_support.get("level") in {"supportive", "moderate"}:
|
||||
secondary_context.append("synastry_support")
|
||||
shadbala_component_audit = present.get("shadbala_component_audit") or {}
|
||||
if shadbala_component_audit.get("status") in {"blocked", "incomplete"}:
|
||||
secondary_context.append("shadbala_component_gap")
|
||||
@@ -946,6 +1033,7 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
"narayana_current",
|
||||
"darakaraka",
|
||||
"upapada_lagna",
|
||||
"synastry_relationship_support",
|
||||
"argala_support",
|
||||
)
|
||||
if present.get(key)
|
||||
@@ -1110,10 +1198,11 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
}
|
||||
present["shadbala"] = _safe_get(modules, "shadbala", "planets")
|
||||
present["shadbala_component_audit"] = _derive_shadbala_component_audit(present["shadbala"]) if present["shadbala"] else None
|
||||
present["kakshya_career_support"] = _derive_kakshya_career_support(_safe_get(modules, "kakshya"))
|
||||
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", "argala_support", "shadbala", "shadbala_component_audit"
|
||||
"external_activation", "argala_support", "shadbala", "shadbala_component_audit", "kakshya_career_support"
|
||||
} and value in (None, {}, [], "")]
|
||||
convergence = present["career_convergence"] or {}
|
||||
confidence_cap = "medium"
|
||||
@@ -1166,12 +1255,13 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
present["shadbala_component_audit"] = _derive_shadbala_component_audit(present["shadbala"]) if present["shadbala"] else None
|
||||
present["jaimini_timing_support"] = _safe_get(modules, "jaimini", "marriage_timing_support")
|
||||
present["jaimini_marriage_support"] = _derive_jaimini_marriage_support(present)
|
||||
present["synastry_relationship_support"] = _derive_synastry_relationship_support(modules)
|
||||
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", "argala_support", "shadbala", "shadbala_component_audit"}
|
||||
if key not in {"chart", "external_activation", "dignity_guardrail", "jaimini_marriage_support", "jaimini_timing_support", "synastry_relationship_support", "argala_support", "shadbala", "shadbala_component_audit"}
|
||||
and value in (None, {}, [], "")
|
||||
]
|
||||
convergence = present["marriage_convergence"] or {}
|
||||
|
||||
@@ -240,6 +240,20 @@ def _normalize_range_scan_success(
|
||||
}
|
||||
)
|
||||
|
||||
top_event = None
|
||||
if evidence_ledger:
|
||||
top = max(
|
||||
evidence_ledger,
|
||||
key=lambda item: item.get("score") if isinstance(item.get("score"), (int, float)) else float("-inf"),
|
||||
)
|
||||
top_event = {
|
||||
"event_id": top.get("event_id"),
|
||||
"score": top.get("score"),
|
||||
"start": top.get("start"),
|
||||
"end": top.get("end"),
|
||||
"tags": top.get("tags") or [],
|
||||
}
|
||||
|
||||
return {
|
||||
"backend": "vedastro_service_adapter_candidate",
|
||||
"available": True,
|
||||
@@ -247,6 +261,8 @@ def _normalize_range_scan_success(
|
||||
"operation": "range_scan",
|
||||
"domain": request_preview["domain"],
|
||||
"request_preview": request_preview,
|
||||
"event_count": len(evidence_ledger),
|
||||
"top_event": top_event,
|
||||
"evidence_ledger": evidence_ledger,
|
||||
"source_metadata": {
|
||||
"transport": "http_json_service_boundary",
|
||||
|
||||
@@ -96,6 +96,36 @@ def test_career_argala_bridge_uses_tenth_house_as_modifier_only() -> None:
|
||||
assert "argala_support" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_career_kakshya_support_adds_small_score_bump_without_label_override() -> None:
|
||||
base_result = _base_career_result()
|
||||
base_result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
|
||||
"convergence_level": "L1",
|
||||
"probability": "+15-20%",
|
||||
}
|
||||
base = _collect_strict_evidence("career", base_result)
|
||||
result = _base_career_result()
|
||||
result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
|
||||
"convergence_level": "L1",
|
||||
"probability": "+15-20%",
|
||||
}
|
||||
result["modules"]["kakshya"] = {
|
||||
"summary": {"average_strength": 6.7},
|
||||
"planets": {"Sun": {"kakshya_strength": 7.0}},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("career", result)
|
||||
|
||||
assert strict["present_evidence"]["kakshya_career_support"] == {
|
||||
"level": "supportive",
|
||||
"source": "kakshya_career_bridge_v1",
|
||||
"signals": ["kakshya_career_support"],
|
||||
"average_strength": 6.7,
|
||||
}
|
||||
assert strict["event_judgement"]["dominant_label"] == "career_status"
|
||||
assert strict["event_judgement"]["score"] >= base["event_judgement"]["score"]
|
||||
assert "kakshya_career_support" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_career_caps_confidence_when_provided_shadbala_components_are_incomplete() -> None:
|
||||
result = _base_career_result()
|
||||
result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
|
||||
|
||||
@@ -114,6 +114,59 @@ def test_relationship_collects_vedastro_range_scan_as_external_activation_contex
|
||||
assert "external_activation_support" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
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", "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_dignity_guardrail_ignores_non_relevant_planets() -> None:
|
||||
result = _base_relationship_result()
|
||||
result["modules"]["chart"] = {
|
||||
|
||||
@@ -280,6 +280,14 @@ def test_vedastro_service_adapter_can_normalize_mock_range_scan_response() -> No
|
||||
assert report["status"] == "ok"
|
||||
assert report["operation"] == "range_scan"
|
||||
assert report["domain"] == "marriage"
|
||||
assert report["event_count"] == 1
|
||||
assert report["top_event"] == {
|
||||
"event_id": "jupiter_7h_window",
|
||||
"score": 72,
|
||||
"start": "2026-05-01",
|
||||
"end": "2026-06-01",
|
||||
"tags": ["marriage", "transit"],
|
||||
}
|
||||
assert report["evidence_ledger"][0]["event_id"] == "jupiter_7h_window"
|
||||
assert report["evidence_ledger"][0]["domain"] == "marriage"
|
||||
assert report["evidence_ledger"][0]["score"] == 72
|
||||
|
||||
Reference in New Issue
Block a user