From e492cb70347f69f58920f0c220d2927620ae90d4 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 28 Jun 2026 15:21:53 +0800 Subject: [PATCH] Cap finance confidence on incomplete Shadbala components --- ..._component_confidence_cap_v1_2026_06_28.md | 59 +++++++++++++ mcp_server.py | 37 +++++++- tests/test_mcp_strict_workflow_finance.py | 86 +++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 docs/research/shadbala_component_confidence_cap_v1_2026_06_28.md diff --git a/docs/research/shadbala_component_confidence_cap_v1_2026_06_28.md b/docs/research/shadbala_component_confidence_cap_v1_2026_06_28.md new file mode 100644 index 00000000..746e71aa --- /dev/null +++ b/docs/research/shadbala_component_confidence_cap_v1_2026_06_28.md @@ -0,0 +1,59 @@ +# Shadbala Component Confidence Cap v1 + +Date: 2026-06-28 + +## Scope + +This pass connects Shadbala quality into the finance strict adjudicator. + +Before this change, `shadbala.planets` acted mostly as a presence gate. A planet with only `total_rupa` could satisfy the route even when the six classical components were missing. + +## Required Components + +The v1 audit requires each listed planet to expose all six components: + +- `sthana` +- `dig` +- `kala` +- `chesta` +- `naisargika` +- `drik` + +Accepted layouts: + +- `planet.components.sthana` +- direct `planet.sthana` + +## Contract + +`present_evidence.shadbala_component_audit`: + +```json +{ + "status": "complete | incomplete | blocked", + "source": "shadbala.planets", + "required_components": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"], + "missing": { + "Venus": ["sthana", "dig"] + } +} +``` + +## Adjudication Boundary + +- `complete`: no confidence penalty. +- `incomplete` or `blocked`: `confidence_cap = "low"` and `secondary_context += ["shadbala_component_gap"]`. + +This does not claim Shadbala absolute calibration is complete. It only prevents a partial Shadbala packet from supporting high-confidence finance timing. + +## Anti-Overclaim Rule + +This v1 pass does not: + +- compare Rupa values against JHora/PyJHora absolute truth +- score individual components +- change `dominant_label` or `payout_label` +- affect career or relationship routes + +External oracle closure is still required before claiming mature Shadbala absolute accuracy. + diff --git a/mcp_server.py b/mcp_server.py index d5864450..912c98ca 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -142,6 +142,7 @@ _NAKSHATRA_LORDS = [ ] _WEALTH_HOUSES = {2, 5, 9, 10, 11} _NAKSHATRA_SPAN = 360.0 / 27.0 +_SHADBALA_REQUIRED_COMPONENTS = ["sthana", "dig", "kala", "chesta", "naisargika", "drik"] def _normalize_longitude(value: Any) -> Optional[float]: @@ -540,6 +541,34 @@ def _derive_ashtakavarga_finance_support(house_scores: Any) -> Dict[str, Any]: } +def _derive_shadbala_component_audit(planets: Any) -> Dict[str, Any]: + audit = { + "status": "blocked", + "source": "shadbala.planets", + "required_components": _SHADBALA_REQUIRED_COMPONENTS, + "missing": {}, + } + if not isinstance(planets, dict) or not planets: + return audit + + missing: Dict[str, List[str]] = {} + for planet, pdata in planets.items(): + if not isinstance(pdata, dict): + missing[str(planet)] = list(_SHADBALA_REQUIRED_COMPONENTS) + continue + components = pdata.get("components") if isinstance(pdata.get("components"), dict) else pdata + planet_missing = [ + component for component in _SHADBALA_REQUIRED_COMPONENTS + if components.get(component) in (None, "", [], {}) + ] + if planet_missing: + missing[str(planet)] = planet_missing + + audit["missing"] = missing + audit["status"] = "complete" if not missing else "incomplete" + return audit + + def _sign_to_index(sign: str) -> Optional[int]: try: return _SIGNS.index(sign) @@ -901,6 +930,9 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s secondary_context.append("gains_wishes") if isinstance(avayogi_risk, dict) and avayogi_risk.get("risk_level") == "moderate": secondary_context.append("avayogi_active") + 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 ashtakavarga_finance.get("level") == "supportive": secondary_context.append("ashtakavarga_wealth_support") elif ashtakavarga_finance.get("level") == "obstructive": @@ -1076,13 +1108,14 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An "wealth_promise_strength": _derive_wealth_promise_strength(modules), "avayogi_risk": avayogi_risk, } + present["shadbala_component_audit"] = _derive_shadbala_component_audit(present["shadbala"]) present["ashtakavarga_finance_support"] = _derive_ashtakavarga_finance_support( present["ashtakavarga_house_scores"] ) present["external_activation"] = _derive_external_activation_support(modules, "wealth") 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", "gains_convergence", "career_convergence", "avayogi_risk", "ashtakavarga_finance_support" + "chart", "external_activation", "dignity_guardrail", "gains_convergence", "career_convergence", "avayogi_risk", "ashtakavarga_finance_support", "shadbala_component_audit" } and value in (None, {}, [], "")] convergence_hits: List[Dict[str, Any]] = [ item for item in [ @@ -1097,6 +1130,8 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An confidence_cap = "low" elif present["dignity_guardrail"].get("status") == "conflict": confidence_cap = "low" + elif present["shadbala_component_audit"].get("status") in {"blocked", "incomplete"}: + confidence_cap = "low" elif any(hit.get("convergence_level") in {"L4", "L5"} for hit in convergence_hits): confidence_cap = "medium-high" elif convergence_hits: diff --git a/tests/test_mcp_strict_workflow_finance.py b/tests/test_mcp_strict_workflow_finance.py index 2752fdee..b3e80aaa 100644 --- a/tests/test_mcp_strict_workflow_finance.py +++ b/tests/test_mcp_strict_workflow_finance.py @@ -228,6 +228,92 @@ def test_collect_strict_evidence_finance_folds_wealth_ashtakavarga_support() -> assert "ashtakavarga_wealth_support" in strict["event_judgement"]["secondary_context"] +def test_collect_strict_evidence_finance_caps_confidence_when_shadbala_components_missing() -> None: + result = { + "modules": { + "varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}}, + "shadbala": {"planets": {"Venus": {"total_rupa": 8.2}}}, + "ashtakavarga": {"house_scores": {"2": 33, "11": 35}}, + "dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Mercury"}}, + "narayana_dasha": {"current_dasha": {"sign": "Taurus", "lord": "Venus"}}, + "dasa_convergence": { + "domain_activations": { + "wealth_family": {"convergence_level": "L4", "probability": "70-85%"}, + "gains_wishes": {"convergence_level": "L4", "probability": "70-85%"}, + "career_status": {"convergence_level": "L4", "probability": "70-85%"}, + } + }, + "yogas_doshas": { + "dhana_yogas": { + "yogas": [{"type": "Dhana Yoga", "strength": "strong"}], + "summary": "Dhana Yoga检测:共1个格局", + } + }, + } + } + + strict = _collect_strict_evidence("finance", result) + + assert strict["present_evidence"]["shadbala_component_audit"] == { + "status": "incomplete", + "source": "shadbala.planets", + "required_components": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"], + "missing": {"Venus": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]}, + } + assert strict["confidence_cap"] == "low" + assert "shadbala_component_gap" in strict["event_judgement"]["secondary_context"] + + +def test_collect_strict_evidence_finance_accepts_complete_shadbala_components() -> None: + result = { + "modules": { + "varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}}, + "shadbala": { + "planets": { + "Venus": { + "components": { + "sthana": 1.2, + "dig": 0.8, + "kala": 1.1, + "chesta": 0.9, + "naisargika": 0.7, + "drik": 0.4, + }, + "total_rupa": 5.1, + } + } + }, + "ashtakavarga": {"house_scores": {"2": 33, "11": 35}}, + "dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Mercury"}}, + "narayana_dasha": {"current_dasha": {"sign": "Taurus", "lord": "Venus"}}, + "dasa_convergence": { + "domain_activations": { + "wealth_family": {"convergence_level": "L4", "probability": "70-85%"}, + "gains_wishes": {"convergence_level": "L4", "probability": "70-85%"}, + "career_status": {"convergence_level": "L4", "probability": "70-85%"}, + } + }, + "yogas_doshas": { + "dhana_yogas": { + "yogas": [{"type": "Dhana Yoga", "strength": "strong"}], + "summary": "Dhana Yoga检测:共1个格局", + } + }, + } + } + + strict = _collect_strict_evidence("finance", result) + + assert strict["present_evidence"]["shadbala_component_audit"] == { + "status": "complete", + "source": "shadbala.planets", + "required_components": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"], + "missing": {}, + } + assert strict["confidence_cap"] == "medium-high" + assert "shadbala_component_gap" not in strict["event_judgement"]["secondary_context"] + + def test_collect_strict_evidence_finance_flags_low_wealth_ashtakavarga_friction() -> None: result = { "modules": {