fix(synastry): close the second review gaps on evidence, billing, and the sidebar

The second review still failed on missing database and route coverage, an unmarked relationship structure, a stuck English lease, and a sidebar that fetched the product switch itself.

Co-Authored-By: Grok (agent loop)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jesse-ux
2026-10-19 14:48:11 +08:00
co-authored by Grok (agent loop) Cursor
parent 51bb3c1e7f
commit 362d903c31
25 changed files with 750 additions and 80 deletions
@@ -96,6 +96,91 @@ def upcoming_vimshottari(reading: dict[str, Any], today: str, years: int = 5) ->
return rows
_SIGNS = (
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
)
_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",
}
def _reading_packet(reading: dict[str, Any]) -> dict[str, Any]:
nested = reading.get("raw_full_reading")
return nested if isinstance(nested, dict) else reading
def _sign_name(value: Any) -> str | None:
if isinstance(value, str) and value.strip():
return value.strip()
if not isinstance(value, dict):
return None
for key in ("sign", "sign_name", "rasi"):
sign = value.get(key)
if isinstance(sign, str) and sign.strip():
return sign.strip()
for key in ("longitude", "degree_raw", "lon", "degree"):
longitude = value.get(key)
if isinstance(longitude, (int, float)) and not isinstance(longitude, bool):
return _SIGNS[int(float(longitude) % 360.0) // 30]
return None
def _named_planet(root: Any, path: tuple[str, ...]) -> str | None:
value = root
for key in path:
value = value.get(key) if isinstance(value, dict) else None
if isinstance(value, str) and value.strip():
return value.strip()
if isinstance(value, dict):
planet = value.get("planet") or value.get("name")
if isinstance(planet, str) and planet.strip():
return planet.strip()
return None
def relationship_structure(reading: dict[str, Any]) -> dict[str, Any]:
"""Signs and relationship points the longitude-only snapshot does not carry."""
modules = _reading_packet(reading).get("modules")
modules = modules if isinstance(modules, dict) else {}
chart = modules.get("chart") if isinstance(modules.get("chart"), dict) else {}
planets = chart.get("planets") if isinstance(chart.get("planets"), dict) else {}
ascendant = _sign_name(chart.get("ascendant"))
seventh = _SIGN_LORDS[_SIGNS[(_SIGNS.index(ascendant) + 6) % 12]] if ascendant in _SIGN_LORDS else None
jaimini = modules.get("jaimini") if isinstance(modules.get("jaimini"), dict) else {}
darakaraka = (
_named_planet(jaimini, ("chara_karaka_standard", "karakas", "DK"))
or _named_planet(jaimini, ("chara_karaka_8", "karaka_table_8", "Darakaraka"))
or _named_planet(jaimini, ("chara_karaka_7", "karaka_table", "Darakaraka"))
)
special = modules.get("special_lagnas") if isinstance(modules.get("special_lagnas"), dict) else {}
arudha = modules.get("arudha_padas") if isinstance(modules.get("arudha_padas"), dict) else {}
padas = arudha.get("padas") if isinstance(arudha.get("padas"), dict) else arudha
upapada = _sign_name(special.get("Upapada_Lagna") or special.get("UL"))
if upapada is None and isinstance(padas, dict):
upapada = _sign_name(padas.get("UL"))
a7 = _sign_name(special.get("A7") or special.get("Darapada"))
if a7 is None and isinstance(padas, dict):
a7 = _sign_name(padas.get("A7"))
vargas = modules.get("varga_full") if isinstance(modules.get("varga_full"), dict) else {}
navamsa = vargas.get("D9_Navamsa") if isinstance(vargas.get("D9_Navamsa"), dict) else {}
d9 = _sign_name(navamsa.get("Ascendant") or navamsa.get("ascendant"))
return {
"ascendant": {"sign": ascendant} if ascendant else {},
"planets": {
"Venus": {"sign": _sign_name(planets.get("Venus"))} if _sign_name(planets.get("Venus")) else {},
"Moon": {"sign": _sign_name(planets.get("Moon"))} if _sign_name(planets.get("Moon")) else {},
},
"seventh_lord": seventh,
"darakaraka": darakaraka,
"upapada": upapada,
"A7": a7,
"d9": {"ascendant": d9} if d9 else {},
}
def build_synastry_full_dossier(
handler: Any,
person_a: dict[str, Any],
@@ -111,6 +196,11 @@ def build_synastry_full_dossier(
reading_a = future_a.result()
reading_b = future_b.result()
dossier = _dossier_builder()(reading_a, reading_b)
if isinstance(dossier, dict):
dossier["local_structures"] = {
"person_a": relationship_structure(reading_a if isinstance(reading_a, dict) else {}),
"person_b": relationship_structure(reading_b if isinstance(reading_b, dict) else {}),
}
today = str(person_a.get("today") or person_b.get("today") or "")
snapshots = dossier.get("chart_snapshots") if isinstance(dossier, dict) else {}
snapshots = snapshots if isinstance(snapshots, dict) else {}
@@ -36,6 +36,31 @@ SECTION_ORDER = (
"practices",
)
PROSE_SECTIONS = SECTION_ORDER[:7]
ZH_SECTION_MINIMUM = 120
ZH_TOTAL_MINIMUM = 1800
EN_SECTION_MINIMUM = 80
EN_TOTAL_MINIMUM = 1100
STATUS_ONLY_VALUES = {
"ready_for_comparison",
"executed",
"present",
"used",
"available",
"passed",
"parameter_sensitive",
"partial",
"weak",
"observation_only",
"not_applicable",
"not_used",
"not_used_for_date_intersection",
"blocked",
"missing",
"queued",
"running",
"ready",
"failed",
}
EVIDENCE_LABELS = (
"multi_system_consensus",
"single_system_inference",
@@ -148,7 +173,7 @@ def _string_values(value: Any) -> set[str]:
found: set[str] = set()
if isinstance(value, str):
stripped = value.strip()
if stripped:
if stripped and stripped.lower() not in STATUS_ONLY_VALUES:
found.add(stripped)
elif isinstance(value, dict):
for item in value.values():
@@ -231,7 +256,7 @@ def validate_synastry_reader(
text = str(row.get("text") or "")
prose_characters += len(re.sub(r"\s+", "", text))
prose_words += len(re.findall(r"\b[\w']+\b", text))
minimum = 120 if language == "zh" else 80
minimum = ZH_SECTION_MINIMUM if language == "zh" else EN_SECTION_MINIMUM
measured = len(re.sub(r"\s+", "", text)) if language == "zh" else len(re.findall(r"\b[\w']+\b", text))
if measured < minimum:
failures.append(_fail(section_id, "too_short", str(measured)))
@@ -290,7 +315,7 @@ def validate_synastry_reader(
if not evidence or evidence not in practice_values:
failures.append(_fail("practices", "evidence_missing", evidence or "item"))
total_minimum = 1800 if language == "zh" else 1100
total_minimum = ZH_TOTAL_MINIMUM if language == "zh" else EN_TOTAL_MINIMUM
total = prose_characters if language == "zh" else prose_words
if total < total_minimum:
failures.append(_fail("delivery", "too_short", str(total)))