Files
Jyotisha/tests/test_full_synastry_execution_contract.py
T
jesse-uxGrok (agent loop)Cursor
e8793bb0d2 fix(synastry): fill section evidence and stop the English job from sticking
The review found empty sections and an English job that stayed running. Take evidence from reader briefs, retry one section, and settle before marking the report ready.

Co-Authored-By: Grok (agent loop)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 12:55:17 +08:00

141 lines
6.4 KiB
Python

from __future__ import annotations
import importlib.util
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE_PATH = ROOT / "skills" / "jyotish-full-synastry-reading" / "scripts" / "validate_full_synastry_packets.py"
spec = importlib.util.spec_from_file_location("full_synastry_execution", MODULE_PATH)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
def _packet(*, ayanamsa="lahiri", blocked=None, parameter_sensitive=None):
modules = {"chart": {"houses": {}}, "functional_benefic_malefic": {}}
for _, candidates, _ in module.REQUIRED_TECHNIQUES:
root, *rest = candidates[0].split(".")
root_status = (
"blocked" if candidates[0] == blocked
else "observation_only" if candidates[0] == parameter_sensitive
else None
)
modules.setdefault(root, {"status": root_status} if root_status else {})
cursor = modules[root]
for key in rest[:-1]:
cursor = cursor.setdefault(key, {})
if rest:
status = (
"blocked" if candidates[0] == blocked
else "observation_only" if candidates[0] == parameter_sensitive
else None
)
cursor[rest[-1]] = {"status": status} if status else {}
return {
"birth_info": {"ayanamsa_name": ayanamsa, "node_mode": "mean"},
"calculation_profile": {
"zodiac": "sidereal",
"position_mode": "legacy",
"house_system": "whole_sign",
},
"modules": modules,
}
def test_full_synastry_receipt_requires_every_registered_technique_for_both_people():
receipt = module.build_execution_receipt(_packet(), _packet())
assert receipt["profile_gate"] == "passed"
assert len(receipt["individual_technique_audit"]) == len(module.REQUIRED_TECHNIQUES)
by_technique = {row["technique"]: row["status"] for row in receipt["individual_technique_audit"]}
assert by_technique["Niryaana Shoola Dasha"] == "not_applicable"
assert {status for technique, status in by_technique.items() if technique != "Niryaana Shoola Dasha"} == {"executed"}
statuses = {task["task"]: task["status"] for task in receipt["pair_execution_tasks"]}
assert statuses["D1/D9 mutual overlays and relationship axes"] == "ready_for_comparison"
assert statuses["DK, UL, UL-second and A7/Darapada cross-comparison"] == "ready_for_comparison"
assert statuses["two-person Dasha/Narayana/Chara context comparison"] == "ready_for_comparison"
assert statuses["shared transit, Tajika, Saham and annual-window comparison"] == "ready_for_comparison"
assert statuses["Nakshatra relationship context"] == "ready_for_comparison"
assert statuses["optional 27-nakshatra Shukuyo narrative layer"] == "not_applicable"
assert statuses["Prashna context when a real question chart was supplied"] == "not_applicable"
def test_shared_life_pair_task_uses_the_native_d4_turyamsa_key():
a, b = _packet(), _packet()
for packet in (a, b):
packet["modules"].setdefault("varga_full", {}).update({
"D2_Hora": {}, "D4_Turyamsa": {}, "D10_Dasamsa": {},
})
packet["modules"]["bhava_bala"] = {}
packet["modules"]["ashtakavarga"] = {}
receipt = module.build_execution_receipt(a, b)
shared_life = next(
row for row in receipt["pair_execution_tasks"]
if row["task"].startswith("shared gain and cost")
)
assert shared_life["status"] == "ready_for_comparison"
assert "modules.varga_full.D4_Turyamsa" in {
row["source_path"] for row in shared_life["required_inputs"]
}
def test_full_synastry_receipt_preserves_blocked_module_and_profile_mismatch():
receipt = module.build_execution_receipt(
_packet(ayanamsa="lahiri"),
_packet(ayanamsa="raman", blocked="dasha"),
)
dasha = next(row for row in receipt["individual_technique_audit"] if row["technique"] == "Vimshottari")
assert receipt["profile_gate"] == "blocked"
assert dasha["status"] == "blocked"
assert dasha["person_b"]["status"] == "blocked"
def test_full_synastry_receipt_keeps_observation_only_technique_out_of_completed_rows():
receipt = module.build_execution_receipt(
_packet(parameter_sensitive="punarphoo"),
_packet(parameter_sensitive="punarphoo"),
)
punarphoo = next(row for row in receipt["individual_technique_audit"] if row["technique"] == "Punarphoo")
assert punarphoo["status"] == "parameter_sensitive"
assert punarphoo["person_a"]["status"] == "parameter_sensitive"
def test_synastry_surfaces_lead_with_evidence_not_a_match_score():
# 原值: 读取 jyotish-app/main.js 与 export.js 里的合盘标题和 Ashtakoota 折叠
# 新值: 读取当前阅读页和技术依据
# 原因: 本仓库没有 jyotish-app。合盘已经改成报告阅读页,传统配对只作参考,不决定结论。
view = (ROOT / "frontend" / "src" / "components" / "synastry" / "synastry-report-view.tsx").read_text(encoding="utf-8")
technical = (ROOT / "frontend" / "src" / "lib" / "synastry-full-technical.ts").read_text(encoding="utf-8")
assert "技术依据摘要" in view
assert "只作一层参考,不决定结论" in technical
assert "computeSynastry?.({ male_moon" not in view
def test_full_synastry_skill_uses_jyotish_judgement_order_and_full_receipt_appendix():
shape = (
ROOT / "skills" / "jyotish-full-synastry-reading" / "references" / "customer-report-shape.md"
).read_text(encoding="utf-8")
skill = (ROOT / "skills" / "jyotish-full-synastry-reading" / "SKILL.md").read_text(encoding="utf-8")
for heading in (
"命局承诺",
"配偶与婚姻承载",
"两盘交感",
"能否久处",
"关系应期:四类事件,四套门槛",
"私有证据包",
"情绪安全与相处感受",
"共同生活的收益与压力",
):
assert heading in shape
assert "individual promise ->\nspouse/marriage carrying" in skill
assert "every row" in skill
def test_synastry_skill_default_prompt_routes_to_the_full_non_score_product():
agent = (ROOT / "skills" / "jyotish-full-synastry-reading" / "agents" / "openai.yaml").read_text(encoding="utf-8")
assert "local birth date" in agent
assert "private full-synastry dossier" in agent
assert "independently composed Chinese and English reports" in agent
assert "coincident Dasha dates" in agent