Bridge synastry support and enrich VedAstro range scan

This commit is contained in:
732642856
2026-06-28 16:37:07 +08:00
parent 76525ac09b
commit e869f561ff
7 changed files with 266 additions and 2 deletions
+1
View File
@@ -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
View File
@@ -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 {}
+16
View File
@@ -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",
+30
View File
@@ -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