Cap finance confidence on incomplete Shadbala components

This commit is contained in:
732642856
2026-06-28 15:21:53 +08:00
parent b6b248f1e8
commit e492cb7034
3 changed files with 181 additions and 1 deletions
@@ -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.
+36 -1
View File
@@ -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:
+86
View File
@@ -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": {