diff --git a/frontend/src/mastra/index.ts b/frontend/src/mastra/index.ts index c75929ec..3c2208df 100644 --- a/frontend/src/mastra/index.ts +++ b/frontend/src/mastra/index.ts @@ -113,7 +113,7 @@ When reference_transparency is present: - Present candidate_windows and exact_triggers when relevant, but describe exact_triggers as technical trigger points, never guaranteed events. - Share a public case only when similar_public_cases.status is high_similarity_public_references_available. State the listed matching factors, dissimilar factors, event source URL, and that the case is reference-only. - If a shared case has reference_status public_context_only, state that it has not been replayed for calibration and cannot increase timing confidence. -- Treat similarity.timing_state as authoritative: only status matched permits saying Vimshottari Mahadasha matches; different or not_compared must be described as such, and never imply Antardasha, Narayana, or transits also match. +- Treat similarity.timing_state as authoritative: matched means Vimshottari MD and AD both match; partial_match means only MD matches; different or not_compared must be described as such. Never imply Narayana or transits also match. - When similar_public_cases.coverage.requested_uncovered_domains is non-empty, say the current public-case catalog does not yet cover those themes; do not infer that no comparable real-world case exists. - When method_variants applies, present parallel methods and their source paths rather than silently picking one result as the only truth. - If should_lead_with_limitations is false, do not lead with limitations. If a limitation is relevant, put it in one short sentence at the end. diff --git a/frontend/tests/consultation-context.test.ts b/frontend/tests/consultation-context.test.ts index b607698b..3daa54b4 100644 --- a/frontend/tests/consultation-context.test.ts +++ b/frontend/tests/consultation-context.test.ts @@ -10,5 +10,6 @@ test("passes transparent public-case references into the agent context", () => { assert.match(source, /requested_uncovered_domains/); assert.match(source, /public_context_only/); assert.match(source, /timing_state/); + assert.match(source, /partial_match/); assert.match(source, /exact_triggers as technical trigger points/); }); diff --git a/scripts/reference_transparency_contract.py b/scripts/reference_transparency_contract.py index 90a19bc3..62b67283 100644 --- a/scripts/reference_transparency_contract.py +++ b/scripts/reference_transparency_contract.py @@ -18,10 +18,12 @@ try: from scripts.domain_calculation_service import compute_chart, compute_vimshottari_timeline from scripts.timing_precision_contract import build_timing_precision_contract from scripts.varga import SIGN_LORDS, calc_varga + from scripts.dasha_analyzer import build_antardasha except ModuleNotFoundError: # pragma: no cover - direct script execution from domain_calculation_service import compute_chart, compute_vimshottari_timeline from timing_precision_contract import build_timing_precision_contract from varga import SIGN_LORDS, calc_varga + from dasha_analyzer import build_antardasha ROOT = Path(__file__).resolve().parents[1] @@ -43,6 +45,7 @@ FEATURE_WEIGHTS = { "d10_ascendant": 0.10, "d10_sun": 0.10, "vimshottari_mahadasha": 0.15, + "vimshottari_antardasha": 0.10, } HIGH_SIMILARITY_THRESHOLD = 0.75 @@ -102,7 +105,7 @@ def _birth_datetime(chart: dict[str, Any]) -> datetime | None: return None -def _vimshottari_mahadasha(chart: dict[str, Any], reference_date: str | None) -> str | None: +def _vimshottari_state(chart: dict[str, Any], reference_date: str | None) -> dict[str, str] | None: if not isinstance(reference_date, str): return None try: @@ -118,7 +121,12 @@ def _vimshottari_mahadasha(chart: dict[str, Any], reference_date: str | None) -> start = datetime.fromisoformat(period["start"]) end = datetime.fromisoformat(period["end"]) if start <= target <= end: - return period["lord"] + antardashas = build_antardasha({"lord": period["lord"], "start": start, "end": end}) + antardasha = next( + (item["lord"] for item in antardashas if item["start"] <= target <= item["end"]), + None, + ) + return {"mahadasha": period["lord"], "antardasha": antardasha} except (KeyError, TypeError, ValueError): return None return None @@ -181,15 +189,22 @@ def _similarity( ) -> dict[str, Any]: user = _features(user_chart, domain) candidate = _features(case_chart, domain) - user_md = _vimshottari_mahadasha(user_chart, reference_date) - case_md = _vimshottari_mahadasha(case_chart, case_event_date) - if user_md is not None and case_md is not None: - user["vimshottari_mahadasha"] = user_md - candidate["vimshottari_mahadasha"] = case_md + user_dasha = _vimshottari_state(user_chart, reference_date) + case_dasha = _vimshottari_state(case_chart, case_event_date) + if user_dasha is not None and case_dasha is not None: + user["vimshottari_mahadasha"] = user_dasha["mahadasha"] + candidate["vimshottari_mahadasha"] = case_dasha["mahadasha"] + if user_dasha.get("antardasha") is not None and case_dasha.get("antardasha") is not None: + user["vimshottari_antardasha"] = user_dasha["antardasha"] + candidate["vimshottari_antardasha"] = case_dasha["antardasha"] + md_matches = user_dasha["mahadasha"] == case_dasha["mahadasha"] + ad_matches = user_dasha.get("antardasha") == case_dasha.get("antardasha") timing_state = { - "status": "matched" if user_md == case_md else "different", - "user_vimshottari_mahadasha": user_md, - "case_event_vimshottari_mahadasha": case_md, + "status": "matched" if md_matches and ad_matches else "partial_match" if md_matches else "different", + "user_vimshottari_mahadasha": user_dasha["mahadasha"], + "case_event_vimshottari_mahadasha": case_dasha["mahadasha"], + "user_vimshottari_antardasha": user_dasha.get("antardasha"), + "case_event_vimshottari_antardasha": case_dasha.get("antardasha"), "reference_date": reference_date, "case_event_date": case_event_date, } @@ -212,9 +227,10 @@ def _similarity( compared_vargas.append("D9") if domain == "career" and all(user.get(name) is not None and candidate.get(name) is not None for name in ("d10_ascendant", "d10_sun")): compared_vargas.append("D10") - uncompared = ["vimshottari_antardasha", "narayana_dasha", "transit_event_state"] + uncompared = ["narayana_dasha", "transit_event_state"] if timing_state["status"] == "not_compared": uncompared.insert(0, "vimshottari_mahadasha") + uncompared.insert(1, "vimshottari_antardasha") if domain == "marriage" and "D9" not in compared_vargas: uncompared.insert(0, "D9") if domain == "career" and "D10" not in compared_vargas: @@ -223,7 +239,7 @@ def _similarity( "score": score, "matching_factors": matching, "dissimilar_factors": dissimilar, - "feature_scope": "D1 ascendant, Moon, theme-house lord, Rahu/Ketu axis" + (f", {'/'.join(compared_vargas)}" if compared_vargas else "") + (", Vimshottari MD" if timing_state["status"] != "not_compared" else ""), + "feature_scope": "D1 ascendant, Moon, theme-house lord, Rahu/Ketu axis" + (f", {'/'.join(compared_vargas)}" if compared_vargas else "") + (", Vimshottari MD/AD" if timing_state["status"] != "not_compared" else ""), "uncompared_layers": uncompared, "timing_state": timing_state, } diff --git a/tests/test_reference_transparency_contract.py b/tests/test_reference_transparency_contract.py index a0cda346..c6331a84 100644 --- a/tests/test_reference_transparency_contract.py +++ b/tests/test_reference_transparency_contract.py @@ -122,9 +122,9 @@ def test_same_event_date_compares_vimshottari_mahadasha() -> None: ) similarity = selected["cases"][0]["similarity"] - assert "vimshottari_mahadasha" in similarity["matching_factors"] + assert {"vimshottari_mahadasha", "vimshottari_antardasha"}.issubset(similarity["matching_factors"]) assert similarity["timing_state"]["status"] == "matched" - assert "vimshottari_antardasha" in similarity["uncompared_layers"] + assert "vimshottari_antardasha" not in similarity["uncompared_layers"] def test_pending_health_case_is_context_only_not_calibration() -> None: