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>
141 lines
6.4 KiB
Python
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
|