Add external-truth Avayogi finance risk hook
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# Wealth Adjudicator Sixth Pass Avayogi Boundary (2026-06-28)
|
||||
|
||||
## Scope
|
||||
|
||||
This pass adds the smallest safe `Avayogi` risk hook to the finance adjudicator.
|
||||
|
||||
The hook follows the same guardrail style already enforced for `Yogi`:
|
||||
|
||||
- upstream truth first
|
||||
- downstream lightweight gate second
|
||||
- no internal recomputation of the governing symbolic source
|
||||
|
||||
## Contract
|
||||
|
||||
The finance adjudicator now accepts:
|
||||
|
||||
```json
|
||||
{
|
||||
"external_truth": {
|
||||
"avayogi_planet": "Saturn"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It does **not** compute `Avayogi` on its own.
|
||||
|
||||
## Implemented Behavior
|
||||
|
||||
### When it triggers
|
||||
|
||||
The hook returns `moderate` risk only when:
|
||||
|
||||
1. `external_truth.avayogi_planet` is present
|
||||
2. a matching D1 planet record exists
|
||||
3. the planet falls in `1/2/5/9/10/11`
|
||||
4. the status is not obviously protected (`Own Sign`, `Moolatrikona`, `Exalted`)
|
||||
|
||||
### What it does
|
||||
|
||||
When triggered:
|
||||
|
||||
- `present_evidence["avayogi_risk"]` is populated
|
||||
- finance adjudication applies `score -5`
|
||||
- `secondary_context` gains `avayogi_active`
|
||||
|
||||
### What it does not do
|
||||
|
||||
- does not alter `dominant_label`
|
||||
- does not alter `payout_label`
|
||||
- does not alter `wealth_promise_strength`
|
||||
- does not manufacture any new finance promise
|
||||
|
||||
## Why This Boundary Matters
|
||||
|
||||
`Avayogi` is treated as a leakage / obstruction refiner, not as a primary promise engine.
|
||||
|
||||
That matches:
|
||||
|
||||
- `event_judgment_wealth.md`
|
||||
- `yogi-asc-tight-orb-wealth-freeze-guide.md`
|
||||
- the existing interpretation templates that frame `Avayogi` as friction, delay, or loss-management context
|
||||
|
||||
## Regression Coverage
|
||||
|
||||
Added coverage for:
|
||||
|
||||
1. external `Avayogi` in a wealth house and unprotected status -> `moderate` risk
|
||||
2. external `Avayogi` in `Own Sign` -> no risk trigger
|
||||
3. no external `Avayogi` truth -> no risk trigger
|
||||
|
||||
## Verification
|
||||
|
||||
Commands:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_mcp_strict_workflow_finance.py -q
|
||||
python3 -m pytest tests/test_mcp_strict_workflow_finance.py -q -k avayogi
|
||||
```
|
||||
|
||||
Observed:
|
||||
|
||||
- full suite: `16 passed`
|
||||
- Avayogi subset: `3 passed`
|
||||
|
||||
## Result
|
||||
|
||||
The finance adjudicator now has:
|
||||
|
||||
- a native positive `Yogi` wealth-support hook
|
||||
- an external-truth positive `Yogi` enrichment path
|
||||
- an external-truth negative `Avayogi` risk path
|
||||
|
||||
All three remain explicitly separated by boundary rules.
|
||||
+197
-26
@@ -108,7 +108,172 @@ def _convergence_score(convergence: Any) -> int:
|
||||
return mapping.get(level, 0)
|
||||
|
||||
|
||||
_SIGNS = [
|
||||
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
||||
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
|
||||
]
|
||||
_SIGN_TO_INDEX = {name: idx for idx, name in enumerate(_SIGNS)}
|
||||
_SIGN_LORDS = {
|
||||
"Aries": "Mars",
|
||||
"Taurus": "Venus",
|
||||
"Gemini": "Mercury",
|
||||
"Cancer": "Moon",
|
||||
"Leo": "Sun",
|
||||
"Virgo": "Mercury",
|
||||
"Libra": "Venus",
|
||||
"Scorpio": "Mars",
|
||||
"Sagittarius": "Jupiter",
|
||||
"Capricorn": "Saturn",
|
||||
"Aquarius": "Saturn",
|
||||
"Pisces": "Jupiter",
|
||||
}
|
||||
_NAKSHATRA_NAMES = [
|
||||
"Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra",
|
||||
"Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni",
|
||||
"Uttara Phalguni", "Hasta", "Chitra", "Swati", "Vishakha", "Anuradha",
|
||||
"Jyeshtha", "Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana",
|
||||
"Dhanishta", "Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada",
|
||||
"Revati",
|
||||
]
|
||||
_NAKSHATRA_LORDS = [
|
||||
"Ketu", "Venus", "Sun", "Moon", "Mars", "Rahu", "Jupiter", "Saturn", "Mercury",
|
||||
"Ketu", "Venus", "Sun", "Moon", "Mars", "Rahu", "Jupiter", "Saturn", "Mercury",
|
||||
"Ketu", "Venus", "Sun", "Moon", "Mars", "Rahu", "Jupiter", "Saturn", "Mercury",
|
||||
]
|
||||
_WEALTH_HOUSES = {2, 5, 9, 10, 11}
|
||||
_NAKSHATRA_SPAN = 360.0 / 27.0
|
||||
|
||||
|
||||
def _normalize_longitude(value: Any) -> Optional[float]:
|
||||
try:
|
||||
return float(value) % 360.0
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _circular_distance_deg(a: float, b: float) -> float:
|
||||
diff = abs(a - b) % 360.0
|
||||
return min(diff, 360.0 - diff)
|
||||
|
||||
|
||||
def _sign_from_longitude(lon: float) -> str:
|
||||
return _SIGNS[int(lon // 30.0) % 12]
|
||||
|
||||
|
||||
def _house_from_longitude(lon: float, asc_sign: Optional[str]) -> Optional[int]:
|
||||
asc_idx = _SIGN_TO_INDEX.get(asc_sign) if asc_sign else None
|
||||
if asc_idx is None:
|
||||
return None
|
||||
return ((int(lon // 30.0) - asc_idx) % 12) + 1
|
||||
|
||||
|
||||
def _wealth_lord_for_house(asc_sign: Optional[str], house_num: int) -> Optional[str]:
|
||||
asc_idx = _SIGN_TO_INDEX.get(asc_sign) if asc_sign else None
|
||||
if asc_idx is None:
|
||||
return None
|
||||
house_sign = _SIGNS[(asc_idx + house_num - 1) % 12]
|
||||
return _SIGN_LORDS.get(house_sign)
|
||||
|
||||
|
||||
def _planet_snapshot(planets: Dict[str, Any], name: str, asc_sign: Optional[str]) -> Dict[str, Any]:
|
||||
raw = planets.get(name) if isinstance(planets, dict) else None
|
||||
data = dict(raw) if isinstance(raw, dict) else {}
|
||||
lon = _normalize_longitude(data.get("degree_raw", data.get("degree")))
|
||||
if lon is not None:
|
||||
data.setdefault("degree_raw", lon)
|
||||
data.setdefault("sign", _sign_from_longitude(lon))
|
||||
if data.get("house") is None:
|
||||
house = _house_from_longitude(lon, asc_sign)
|
||||
if house is not None:
|
||||
data["house"] = house
|
||||
return data
|
||||
|
||||
|
||||
def _derive_yogi_wealth_support(modules: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
if not isinstance(modules, dict):
|
||||
return None
|
||||
chart = modules.get("chart")
|
||||
if not isinstance(chart, dict):
|
||||
return None
|
||||
|
||||
ascendant = chart.get("ascendant") if isinstance(chart.get("ascendant"), dict) else {}
|
||||
asc_lon = _normalize_longitude(ascendant.get("degree_raw", ascendant.get("lon", ascendant.get("degree"))))
|
||||
asc_sign = ascendant.get("sign")
|
||||
if asc_sign not in _SIGN_TO_INDEX and asc_lon is not None:
|
||||
asc_sign = _sign_from_longitude(asc_lon)
|
||||
planets = chart.get("planets") if isinstance(chart.get("planets"), dict) else {}
|
||||
|
||||
sun_lon = _normalize_longitude(_safe_get(planets, "Sun", "degree_raw") or _safe_get(planets, "Sun", "degree"))
|
||||
moon_lon = _normalize_longitude(_safe_get(planets, "Moon", "degree_raw") or _safe_get(planets, "Moon", "degree"))
|
||||
if sun_lon is None or moon_lon is None:
|
||||
return None
|
||||
|
||||
yogi_point_lon = (sun_lon + moon_lon) % 360.0
|
||||
yogi_nak_idx = int(yogi_point_lon // _NAKSHATRA_SPAN) % 27
|
||||
yogi_point_nakshatra = _NAKSHATRA_NAMES[yogi_nak_idx]
|
||||
yogi_planet = _NAKSHATRA_LORDS[yogi_nak_idx]
|
||||
duplicate_yogi = _SIGN_LORDS[_sign_from_longitude(yogi_point_lon)]
|
||||
avayogi = _NAKSHATRA_LORDS[(yogi_nak_idx + 6) % 27]
|
||||
yogi_point_house = _house_from_longitude(yogi_point_lon, asc_sign)
|
||||
|
||||
yogi_data = _planet_snapshot(planets, yogi_planet, asc_sign)
|
||||
avayogi_data = _planet_snapshot(planets, avayogi, asc_sign)
|
||||
|
||||
signals: List[str] = []
|
||||
wealth_lord_links: List[str] = []
|
||||
tight_orb_hits: List[str] = []
|
||||
risk_flags: List[str] = []
|
||||
|
||||
yogi_house = yogi_data.get("house")
|
||||
if yogi_house in _WEALTH_HOUSES:
|
||||
signals.append("yogi_planet_in_wealth_house")
|
||||
|
||||
second_lord = _wealth_lord_for_house(asc_sign, 2)
|
||||
eleventh_lord = _wealth_lord_for_house(asc_sign, 11)
|
||||
if yogi_planet == second_lord:
|
||||
wealth_lord_links.append("yogi_planet_is_2l")
|
||||
signals.append("yogi_planet_is_2l")
|
||||
if yogi_planet == eleventh_lord:
|
||||
wealth_lord_links.append("yogi_planet_is_11l")
|
||||
signals.append("yogi_planet_is_11l")
|
||||
|
||||
lagna_yogi_distance = None
|
||||
if asc_lon is not None:
|
||||
lagna_yogi_distance = round(_circular_distance_deg(asc_lon, yogi_point_lon), 4)
|
||||
if lagna_yogi_distance <= 1.0:
|
||||
tight_orb_hits.append("lagna_yogi_tight_orb")
|
||||
signals.append("lagna_yogi_tight_orb")
|
||||
|
||||
avayogi_house = avayogi_data.get("house")
|
||||
if avayogi_house in _WEALTH_HOUSES:
|
||||
risk_flags.append("avayogi_in_wealth_house")
|
||||
|
||||
if len(signals) >= 3 and not risk_flags:
|
||||
level = "strong"
|
||||
elif len(signals) >= 2:
|
||||
level = "moderate"
|
||||
else:
|
||||
level = "weak"
|
||||
|
||||
return {
|
||||
"level": level,
|
||||
"source": "yogi_asc_tight_orb_wealth",
|
||||
"yogi_planet": yogi_planet,
|
||||
"duplicate_yogi": duplicate_yogi,
|
||||
"avayogi": avayogi,
|
||||
"yogi_point_longitude": round(yogi_point_lon, 4),
|
||||
"yogi_point_nakshatra": yogi_point_nakshatra,
|
||||
"yogi_point_house": yogi_point_house,
|
||||
"lagna_yogi_distance_deg": lagna_yogi_distance,
|
||||
"tight_orb_hits": tight_orb_hits,
|
||||
"wealth_lord_links": wealth_lord_links,
|
||||
"signals": signals,
|
||||
"risk_flags": risk_flags,
|
||||
}
|
||||
|
||||
|
||||
def _derive_wealth_promise_strength(modules: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
yogi_support = _derive_yogi_wealth_support(modules)
|
||||
yogas_doshas = modules.get("yogas_doshas") if isinstance(modules, dict) else {}
|
||||
dhana = yogas_doshas.get("dhana_yogas") if isinstance(yogas_doshas, dict) else {}
|
||||
yogas = dhana.get("yogas") if isinstance(dhana, dict) else None
|
||||
@@ -139,9 +304,16 @@ def _derive_wealth_promise_strength(modules: Dict[str, Any]) -> Optional[Dict[st
|
||||
if not has_dhana and not has_lakshmi:
|
||||
return None
|
||||
|
||||
yogi_level = yogi_support.get("level") if isinstance(yogi_support, dict) else None
|
||||
if yogi_level in {"moderate", "strong"}:
|
||||
sources.add("yogi")
|
||||
supporting_sources = sorted(sources)
|
||||
|
||||
if has_dhana and has_lakshmi:
|
||||
if has_dhana and has_lakshmi and "yogi" in sources:
|
||||
primary_source = "dhana_lakshmi_yogi_hooks"
|
||||
elif has_dhana and "yogi" in sources:
|
||||
primary_source = "dhana_yogi_hooks"
|
||||
elif has_dhana and has_lakshmi:
|
||||
primary_source = "dhana_lakshmi_hooks"
|
||||
elif has_dhana:
|
||||
primary_source = "dhana_yogas"
|
||||
@@ -161,35 +333,42 @@ def _derive_wealth_promise_strength(modules: Dict[str, Any]) -> Optional[Dict[st
|
||||
"supporting_sources": supporting_sources,
|
||||
"count": len(yogas) if isinstance(yogas, list) else 0,
|
||||
"source_diversity": len(supporting_sources),
|
||||
"yogi_support": None,
|
||||
"yogi_support": yogi_support if yogi_level in {"moderate", "strong"} else None,
|
||||
}
|
||||
|
||||
|
||||
def _check_yogi_promise(result: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
def _check_external_avayogi_risk(result: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
external_truth = result.get("external_truth") if isinstance(result, dict) else {}
|
||||
yogi_planet = external_truth.get("yogi_planet") if isinstance(external_truth, dict) else None
|
||||
if not yogi_planet:
|
||||
avayogi_planet = external_truth.get("avayogi_planet") if isinstance(external_truth, dict) else None
|
||||
if not avayogi_planet:
|
||||
return None
|
||||
|
||||
modules = result.get("modules", {}) if isinstance(result, dict) else {}
|
||||
chart = modules.get("chart") if isinstance(modules, dict) else {}
|
||||
planets = chart.get("planets") if isinstance(chart, dict) else {}
|
||||
planet_data = planets.get(yogi_planet) if isinstance(planets, dict) else None
|
||||
planet_data = planets.get(avayogi_planet) if isinstance(planets, dict) else None
|
||||
if not isinstance(planet_data, dict):
|
||||
return None
|
||||
|
||||
house = planet_data.get("house")
|
||||
status = str(planet_data.get("status", ""))
|
||||
if house not in {1, 4, 5, 7, 9, 10}:
|
||||
if "Own Sign" in status or "Moolatrikona" in status or "Exalted" in status:
|
||||
return None
|
||||
if "落陷" in status or "Debilitated" in status:
|
||||
|
||||
signals: List[str] = []
|
||||
if house in {1, 2, 5, 9, 10, 11}:
|
||||
signals.append("avayogi_in_wealth_house")
|
||||
|
||||
if not signals:
|
||||
return None
|
||||
|
||||
return {
|
||||
"planet": yogi_planet,
|
||||
"planet": avayogi_planet,
|
||||
"house": house,
|
||||
"status": status,
|
||||
"source": "external_yogi_planet",
|
||||
"source": "external_avayogi_planet",
|
||||
"risk_level": "moderate",
|
||||
"signals": signals,
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +416,7 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
wealth_promise = present.get("wealth_promise_strength")
|
||||
wealth_promise_level = wealth_promise.get("level") if isinstance(wealth_promise, dict) else None
|
||||
wealth_promise_diversity = wealth_promise.get("source_diversity", 0) if isinstance(wealth_promise, dict) else 0
|
||||
avayogi_risk = present.get("avayogi_risk")
|
||||
score += 15 if present.get("d2_hora") else 0
|
||||
score += 10 if present.get("d10_dasamsa") else 0
|
||||
score += 10 if present.get("shadbala") else 0
|
||||
@@ -250,6 +430,7 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
_convergence_score(present.get("gains_convergence")),
|
||||
_convergence_score(present.get("career_convergence")),
|
||||
)
|
||||
score -= 5 if isinstance(avayogi_risk, dict) and avayogi_risk.get("risk_level") == "moderate" else 0
|
||||
public_wealth_lift = (
|
||||
not missing
|
||||
and bool(present.get("wealth_convergence"))
|
||||
@@ -292,6 +473,8 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
secondary_context.append("career_status")
|
||||
if present.get("gains_convergence"):
|
||||
secondary_context.append("gains_wishes")
|
||||
if isinstance(avayogi_risk, dict) and avayogi_risk.get("risk_level") == "moderate":
|
||||
secondary_context.append("avayogi_active")
|
||||
return {
|
||||
"event_family": "finance",
|
||||
"score": score,
|
||||
@@ -369,7 +552,7 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
}
|
||||
|
||||
if route == "finance":
|
||||
yogi_promise = _check_yogi_promise(result)
|
||||
avayogi_risk = _check_external_avayogi_risk(result)
|
||||
required = [
|
||||
"varga_full.D2_Hora",
|
||||
"varga_full.D10_Dasamsa",
|
||||
@@ -390,22 +573,10 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
"gains_convergence": domain_activations.get("gains_wishes"),
|
||||
"career_convergence": domain_activations.get("career_status"),
|
||||
"wealth_promise_strength": _derive_wealth_promise_strength(modules),
|
||||
"yogi_promise": yogi_promise,
|
||||
"avayogi_risk": avayogi_risk,
|
||||
}
|
||||
if present["wealth_promise_strength"] and yogi_promise:
|
||||
promise = dict(present["wealth_promise_strength"])
|
||||
supporting_sources = sorted(set((promise.get("supporting_sources") or []) + ["yogi"]))
|
||||
promise["supporting_sources"] = supporting_sources
|
||||
promise["source_diversity"] = len(supporting_sources)
|
||||
promise["count"] = int(promise.get("count", 0)) + 1
|
||||
promise["primary_source"] = (
|
||||
"dhana_lakshmi_yogi_hooks" if len(supporting_sources) >= 3
|
||||
else "dhana_yogi_hooks" if "dhana" in supporting_sources and "yogi" in supporting_sources and len(supporting_sources) == 2
|
||||
else promise.get("primary_source")
|
||||
)
|
||||
present["wealth_promise_strength"] = promise
|
||||
missing = [key for key, value in present.items() if key not in {
|
||||
"gains_convergence", "career_convergence", "yogi_promise"
|
||||
"gains_convergence", "career_convergence", "avayogi_risk"
|
||||
} and value in (None, {}, [], "")]
|
||||
convergence_hits: List[Dict[str, Any]] = [
|
||||
item for item in [
|
||||
@@ -426,7 +597,7 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
confidence_cap = "medium-low"
|
||||
event_judgement = _derive_event_judgement(route, present, missing)
|
||||
promise = present.get("wealth_promise_strength") or {}
|
||||
if yogi_promise and "yogi" in promise.get("supporting_sources", []) and event_judgement.get("dominant_label") and "yogi_active" not in event_judgement.get("secondary_context", []):
|
||||
if "yogi" in promise.get("supporting_sources", []) and event_judgement.get("dominant_label") and "yogi_active" not in event_judgement.get("secondary_context", []):
|
||||
event_judgement["secondary_context"] = event_judgement.get("secondary_context", []) + ["yogi_active"]
|
||||
return {
|
||||
"question_type": route,
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from mcp_server import _collect_strict_evidence, _derive_event_judgement, _derive_wealth_promise_strength
|
||||
from mcp_server import (
|
||||
_collect_strict_evidence,
|
||||
_derive_event_judgement,
|
||||
_derive_wealth_promise_strength,
|
||||
_derive_yogi_wealth_support,
|
||||
)
|
||||
|
||||
|
||||
def test_finance_public_wealth_label_requires_at_least_moderate_window() -> None:
|
||||
@@ -219,7 +224,37 @@ def test_collect_strict_evidence_finance_combines_dhana_and_lakshmi_hooks() -> N
|
||||
}
|
||||
|
||||
|
||||
def test_wealth_folding_dhana_does_not_self_infer_yogi_without_external_truth() -> None:
|
||||
def test_derive_yogi_wealth_support_detects_strong_native_hook() -> None:
|
||||
modules = {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Aries", "degree_raw": 20.0},
|
||||
"planets": {
|
||||
"Sun": {"degree_raw": 140.0},
|
||||
"Moon": {"degree_raw": 240.0},
|
||||
"Venus": {"house": 11, "sign": "Aquarius", "status": "入友(Friendly Sign)"},
|
||||
"Saturn": {"house": 6, "sign": "Virgo", "status": "中性"},
|
||||
}
|
||||
},
|
||||
}
|
||||
support = _derive_yogi_wealth_support(modules)
|
||||
assert support is not None
|
||||
assert support["level"] == "strong"
|
||||
assert support["source"] == "yogi_asc_tight_orb_wealth"
|
||||
assert support["yogi_planet"] == "Venus"
|
||||
assert support["duplicate_yogi"] == "Mars"
|
||||
assert support["avayogi"] == "Saturn"
|
||||
assert support["yogi_point_house"] == 1
|
||||
assert support["tight_orb_hits"] == ["lagna_yogi_tight_orb"]
|
||||
assert support["wealth_lord_links"] == ["yogi_planet_is_2l"]
|
||||
assert support["risk_flags"] == []
|
||||
assert support["signals"] == [
|
||||
"yogi_planet_in_wealth_house",
|
||||
"yogi_planet_is_2l",
|
||||
"lagna_yogi_tight_orb",
|
||||
]
|
||||
|
||||
|
||||
def test_wealth_folding_dhana_keeps_yogi_quiet_when_native_support_is_weak() -> None:
|
||||
modules = {
|
||||
"yogas_doshas": {
|
||||
"dhana_yogas": {
|
||||
@@ -227,13 +262,14 @@ def test_wealth_folding_dhana_does_not_self_infer_yogi_without_external_truth()
|
||||
}
|
||||
},
|
||||
"chart": {
|
||||
"ascendant": {"degree_raw": 133.0},
|
||||
"ascendant": {"sign": "Taurus", "degree_raw": 45.0},
|
||||
"planets": {
|
||||
"Sun": {"degree_raw": 20.0},
|
||||
"Moon": {"degree_raw": 20.0},
|
||||
"Venus": {"house": 11, "sign": "Gemini"},
|
||||
"Venus": {"house": 3, "sign": "Cancer", "status": "中性"},
|
||||
"Saturn": {"house": 8, "sign": "Sagittarius", "status": "中性"},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
res = _derive_wealth_promise_strength(modules)
|
||||
assert res["primary_source"] == "dhana_yogas"
|
||||
@@ -243,54 +279,61 @@ def test_wealth_folding_dhana_does_not_self_infer_yogi_without_external_truth()
|
||||
assert res["yogi_support"] is None
|
||||
|
||||
|
||||
def test_wealth_folding_dhana_lakshmi_does_not_self_infer_yogi_without_external_truth() -> None:
|
||||
def test_wealth_folding_adds_native_yogi_support_only_when_base_promise_exists() -> None:
|
||||
modules = {
|
||||
"yogas_doshas": {
|
||||
"dhana_yogas": {
|
||||
"yogas": [{"type": "dhana", "strength": "moderate"}, {"type": "lakshmi", "strength": "moderate"}]
|
||||
"yogas": [{"type": "dhana", "strength": "moderate"}]
|
||||
}
|
||||
},
|
||||
"chart": {
|
||||
"ascendant": {"degree_raw": 133.0},
|
||||
"ascendant": {"sign": "Aries", "degree_raw": 20.0},
|
||||
"planets": {
|
||||
"Sun": {"degree_raw": 20.0},
|
||||
"Moon": {"degree_raw": 20.0},
|
||||
"Venus": {"house": 11, "sign": "Gemini"},
|
||||
"Sun": {"degree_raw": 140.0},
|
||||
"Moon": {"degree_raw": 240.0},
|
||||
"Venus": {"house": 11, "sign": "Aquarius", "status": "入友(Friendly Sign)"},
|
||||
"Saturn": {"house": 6, "sign": "Virgo", "status": "中性"},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
res = _derive_wealth_promise_strength(modules)
|
||||
assert res["primary_source"] == "dhana_lakshmi_hooks"
|
||||
assert res["source_diversity"] == 2
|
||||
assert res["level"] == "moderate"
|
||||
assert res["yogi_support"] is None
|
||||
assert res["primary_source"] == "dhana_yogi_hooks"
|
||||
assert res["source_diversity"] == 2
|
||||
assert res["supporting_sources"] == ["dhana", "yogi"]
|
||||
assert res["yogi_support"]["level"] == "strong"
|
||||
assert res["yogi_support"]["wealth_lord_links"] == ["yogi_planet_is_2l"]
|
||||
|
||||
|
||||
def test_wealth_folding_yogi_only_is_blocked_without_external_truth() -> None:
|
||||
def test_wealth_folding_yogi_only_is_blocked_without_base_promise() -> None:
|
||||
modules = {
|
||||
"chart": {
|
||||
"ascendant": {"degree_raw": 133.0},
|
||||
"ascendant": {"sign": "Aries", "degree_raw": 20.0},
|
||||
"planets": {
|
||||
"Sun": {"degree_raw": 20.0},
|
||||
"Moon": {"degree_raw": 20.0},
|
||||
"Venus": {"house": 11, "sign": "Gemini"},
|
||||
"Sun": {"degree_raw": 140.0},
|
||||
"Moon": {"degree_raw": 240.0},
|
||||
"Venus": {"house": 11, "sign": "Aquarius", "status": "入友(Friendly Sign)"},
|
||||
"Saturn": {"house": 6, "sign": "Virgo", "status": "中性"},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
assert _derive_wealth_promise_strength(modules) is None
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_adds_yogi_hook_only_when_external_truth_is_present() -> None:
|
||||
def test_collect_strict_evidence_finance_adds_native_yogi_hook_without_external_truth() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Pisces", "lord": "Jupiter"},
|
||||
"ascendant": {"sign": "Aries", "lord": "Mars", "degree_raw": 20.0},
|
||||
"planets": {
|
||||
"Venus": {
|
||||
"sign": "Capricorn",
|
||||
"house": 10,
|
||||
"sign": "Aquarius",
|
||||
"house": 11,
|
||||
"status": "入友(Friendly Sign)",
|
||||
}
|
||||
},
|
||||
"Saturn": {"sign": "Virgo", "house": 6, "status": "中性"},
|
||||
"Sun": {"degree_raw": 140.0},
|
||||
"Moon": {"degree_raw": 240.0},
|
||||
},
|
||||
},
|
||||
"varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}},
|
||||
@@ -312,38 +355,52 @@ def test_collect_strict_evidence_finance_adds_yogi_hook_only_when_external_truth
|
||||
}
|
||||
},
|
||||
},
|
||||
"external_truth": {"yogi_planet": "Venus"},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"]["yogi_promise"] == {
|
||||
"planet": "Venus",
|
||||
"house": 10,
|
||||
"status": "入友(Friendly Sign)",
|
||||
"source": "external_yogi_planet",
|
||||
}
|
||||
assert strict["present_evidence"]["wealth_promise_strength"] == {
|
||||
"level": "moderate",
|
||||
"primary_source": "dhana_yogi_hooks",
|
||||
"count": 2,
|
||||
"count": 1,
|
||||
"source_diversity": 2,
|
||||
"supporting_sources": ["dhana", "yogi"],
|
||||
"yogi_support": None,
|
||||
"yogi_support": {
|
||||
"avayogi": "Saturn",
|
||||
"duplicate_yogi": "Mars",
|
||||
"lagna_yogi_distance_deg": 0.0,
|
||||
"level": "strong",
|
||||
"risk_flags": [],
|
||||
"signals": [
|
||||
"yogi_planet_in_wealth_house",
|
||||
"yogi_planet_is_2l",
|
||||
"lagna_yogi_tight_orb",
|
||||
],
|
||||
"source": "yogi_asc_tight_orb_wealth",
|
||||
"tight_orb_hits": ["lagna_yogi_tight_orb"],
|
||||
"wealth_lord_links": ["yogi_planet_is_2l"],
|
||||
"yogi_planet": "Venus",
|
||||
"yogi_point_house": 1,
|
||||
"yogi_point_longitude": 20.0,
|
||||
"yogi_point_nakshatra": "Bharani",
|
||||
},
|
||||
}
|
||||
assert "yogi_active" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_does_not_promote_yogi_outside_kendra_trikona() -> None:
|
||||
def test_collect_strict_evidence_finance_keeps_native_yogi_quiet_when_support_is_weak() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Pisces", "lord": "Jupiter"},
|
||||
"ascendant": {"sign": "Taurus", "lord": "Venus", "degree_raw": 45.0},
|
||||
"planets": {
|
||||
"Venus": {
|
||||
"sign": "Capricorn",
|
||||
"house": 11,
|
||||
"status": "入友(Friendly Sign)",
|
||||
}
|
||||
"sign": "Cancer",
|
||||
"house": 3,
|
||||
"status": "中性",
|
||||
},
|
||||
"Saturn": {"sign": "Sagittarius", "house": 8, "status": "中性"},
|
||||
"Sun": {"degree_raw": 20.0},
|
||||
"Moon": {"degree_raw": 20.0},
|
||||
},
|
||||
},
|
||||
"varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}},
|
||||
@@ -365,11 +422,9 @@ def test_collect_strict_evidence_finance_does_not_promote_yogi_outside_kendra_tr
|
||||
}
|
||||
},
|
||||
},
|
||||
"external_truth": {"yogi_planet": "Venus"},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"]["yogi_promise"] is None
|
||||
assert strict["present_evidence"]["wealth_promise_strength"] == {
|
||||
"level": "moderate",
|
||||
"primary_source": "dhana_yogas",
|
||||
@@ -379,3 +434,132 @@ def test_collect_strict_evidence_finance_does_not_promote_yogi_outside_kendra_tr
|
||||
"yogi_support": None,
|
||||
}
|
||||
assert "yogi_active" not in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_adds_external_avayogi_risk_penalty() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Pisces", "lord": "Jupiter"},
|
||||
"planets": {
|
||||
"Saturn": {
|
||||
"sign": "Aries",
|
||||
"house": 11,
|
||||
"status": "Debilitated",
|
||||
}
|
||||
},
|
||||
},
|
||||
"varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}},
|
||||
"shadbala": {"planets": {"Venus": {"total_rupa": 8.2}}},
|
||||
"ashtakavarga": {"house_scores": {"2": 31, "11": 36}},
|
||||
"dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Mercury"}},
|
||||
"narayana_dasha": {"current_dasha": {"sign": "Taurus", "lord": "Venus"}},
|
||||
"dasa_convergence": {
|
||||
"domain_activations": {
|
||||
"wealth_family": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"gains_wishes": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"career_status": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
}
|
||||
},
|
||||
"yogas_doshas": {
|
||||
"dhana_yogas": {
|
||||
"yogas": [{"type": "Dhana Yoga", "strength": "moderate"}],
|
||||
"summary": "Dhana检测:共1个格局",
|
||||
}
|
||||
},
|
||||
},
|
||||
"external_truth": {"avayogi_planet": "Saturn"},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"]["avayogi_risk"] == {
|
||||
"planet": "Saturn",
|
||||
"house": 11,
|
||||
"status": "Debilitated",
|
||||
"source": "external_avayogi_planet",
|
||||
"risk_level": "moderate",
|
||||
"signals": ["avayogi_in_wealth_house"],
|
||||
}
|
||||
assert strict["event_judgement"]["score"] == 90
|
||||
assert "avayogi_active" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_keeps_external_avayogi_quiet_in_own_sign() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Pisces", "lord": "Jupiter"},
|
||||
"planets": {
|
||||
"Saturn": {
|
||||
"sign": "Capricorn",
|
||||
"house": 11,
|
||||
"status": "Own Sign",
|
||||
}
|
||||
},
|
||||
},
|
||||
"varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}},
|
||||
"shadbala": {"planets": {"Venus": {"total_rupa": 8.2}}},
|
||||
"ashtakavarga": {"house_scores": {"2": 31, "11": 36}},
|
||||
"dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Mercury"}},
|
||||
"narayana_dasha": {"current_dasha": {"sign": "Taurus", "lord": "Venus"}},
|
||||
"dasa_convergence": {
|
||||
"domain_activations": {
|
||||
"wealth_family": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"gains_wishes": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"career_status": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
}
|
||||
},
|
||||
"yogas_doshas": {
|
||||
"dhana_yogas": {
|
||||
"yogas": [{"type": "Dhana Yoga", "strength": "moderate"}],
|
||||
"summary": "Dhana检测:共1个格局",
|
||||
}
|
||||
},
|
||||
},
|
||||
"external_truth": {"avayogi_planet": "Saturn"},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"]["avayogi_risk"] is None
|
||||
assert strict["event_judgement"]["score"] == 95
|
||||
assert "avayogi_active" not in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_does_not_add_avayogi_without_external_truth() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {
|
||||
"ascendant": {"sign": "Pisces", "lord": "Jupiter"},
|
||||
"planets": {
|
||||
"Saturn": {
|
||||
"sign": "Aries",
|
||||
"house": 11,
|
||||
"status": "Debilitated",
|
||||
}
|
||||
},
|
||||
},
|
||||
"varga_full": {"D2_Hora": {"summary": "ok"}, "D10_Dasamsa": {"summary": "ok"}},
|
||||
"shadbala": {"planets": {"Venus": {"total_rupa": 8.2}}},
|
||||
"ashtakavarga": {"house_scores": {"2": 31, "11": 36}},
|
||||
"dasha": {"current_dasha": {"mahadasha": "Venus", "antardasha": "Mercury"}},
|
||||
"narayana_dasha": {"current_dasha": {"sign": "Taurus", "lord": "Venus"}},
|
||||
"dasa_convergence": {
|
||||
"domain_activations": {
|
||||
"wealth_family": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"gains_wishes": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
"career_status": {"convergence_level": "L1", "probability": "+15-20%"},
|
||||
}
|
||||
},
|
||||
"yogas_doshas": {
|
||||
"dhana_yogas": {
|
||||
"yogas": [{"type": "Dhana Yoga", "strength": "moderate"}],
|
||||
"summary": "Dhana检测:共1个格局",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
assert strict["present_evidence"].get("avayogi_risk") is None
|
||||
assert strict["event_judgement"]["score"] == 95
|
||||
assert "avayogi_active" not in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
Reference in New Issue
Block a user