New frontend/src/lib/consultation-evidence-card.ts ports the research CARD_SPECS: base section (ascendant, house signs, placements with degrees, functional benefics/malefics with lordship, Vimshottari MD/AD/PD with dates, Narayana md/ad/pd) plus a section per domain; values copied verbatim from the projection or engine context, gaps listed, never filled. The tool now returns toModelEvidenceView: status, evidence_contract (policy, blockers, layers, limitation), claim_cards = the card, evidence_card meta (D4 line, supplementable sections), rectification, methodology, domains; the audit table, spectra, must_use_layers and presentation stay server-side and the single-domain consultations copy is gone. 「本轮技法」 rows are unchanged. Golden tests over three public AA engine captures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
141 lines
6.5 KiB
Python
141 lines
6.5 KiB
Python
"""Pure-function checks for the consultation evidence-card research scripts."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||
|
||
from consult_evidence_card_lib import ( # noqa: E402
|
||
CARD_SPECS,
|
||
CATEGORIES,
|
||
TELEMETRY_SPEC,
|
||
account,
|
||
category_report,
|
||
cut_evidence_card,
|
||
dumps,
|
||
parse_methodology_routes,
|
||
parse_must_use_layers,
|
||
parse_registry_layers,
|
||
verbatim_check,
|
||
)
|
||
|
||
|
||
def test_category_partition_sums_exactly():
|
||
payload = {
|
||
"ascendant": {"sign": "Leo", "degree": 12.5},
|
||
"methodology": {"sections": ["checklist"]},
|
||
"western_spectrum": {"zodiac": "tropical", "natal": {"sun": "Aries"}},
|
||
"varga_spectrum": {
|
||
"formal": {"D1": {"lagna": "Leo"}},
|
||
"research_dn": {"D3": {"lagna": "Virgo"}},
|
||
"extended": {"D81": {"lagna": "Cancer"}},
|
||
},
|
||
"technique_audit_table": [
|
||
{"technique": "Functional Benefic/Malefic", "status": "executed"},
|
||
{"technique": "MEVG / Global Web Evidence", "status": "blocked"},
|
||
{"technique": "Prashna chart", "status": "not_applicable"},
|
||
{"technique": "Research D-N through D60", "status": "executed"},
|
||
{"technique": "Western natal (tropical)", "status": "executed", "system": "western"},
|
||
],
|
||
}
|
||
report = category_report(payload)
|
||
assert report["sum_error_ratio"] == 0.0
|
||
assert report["category_sum"] == report["total_chars"] == len(dumps(payload))
|
||
assert set(report["categories"]) == set(CATEGORIES)
|
||
assert report["categories"]["western"] > 0
|
||
assert report["categories"]["research"] > 0
|
||
assert report["categories"]["status"] > 0
|
||
assert report["categories"]["not_applicable"] > 0
|
||
assert report["categories"]["core"] > 0
|
||
assert sum(account(payload).values()) == len(dumps(payload))
|
||
|
||
|
||
def test_card_copies_projection_values_without_rewriting():
|
||
model = {
|
||
"question": "我和父母关系如何",
|
||
"route": "family",
|
||
"claim_cards": [
|
||
{
|
||
"category": "natal_foundation",
|
||
"evidence": {
|
||
"ascendant": {"sign": "Capricorn", "degree": 3.25},
|
||
"houses": [{"number": 4, "sign": "Aries", "lord": "Mars"}],
|
||
"planets": {"sun": {"sign": "Aquarius", "house": 2}, "moon": {"sign": "Cancer", "house": 7}},
|
||
"functional_benefic_malefic": {"functional_benefics": ["Venus"], "functional_malefics": ["Mars"]},
|
||
"varga_spectrum": {"formal": {"D12": {"lagna": "Pisces", "planets": {"moon": "Leo"}}}},
|
||
},
|
||
},
|
||
{
|
||
"category": "timing",
|
||
"evidence": {
|
||
"dasha_sub_periods": {
|
||
"current": {
|
||
"mahadasha": {"lord": "Saturn", "start": "2020-01-01", "end": "2026-12-31"},
|
||
"antardasha": {"lord": "Mercury", "start": "2024-01-01", "end": "2025-01-01"},
|
||
},
|
||
},
|
||
"narayana_dasha": {"current_dasha": {"md": {"sign": "Scorpio"}}},
|
||
},
|
||
},
|
||
],
|
||
}
|
||
cut = cut_evidence_card(model, "parents")
|
||
assert cut["card"]["domain"]["vargas"]["D12"]["lagna"] == "Pisces"
|
||
assert cut["card"]["base"]["ascendant"]["degree"] == 3.25
|
||
assert "D7" not in cut["card"]["domain"]["vargas"]
|
||
check = verbatim_check(cut["card"], {
|
||
"ascendant_sign": "Capricorn",
|
||
"d12_lagna": "Pisces",
|
||
"d12_moon": "Leo",
|
||
"vimshottari_start": "2020-01-01",
|
||
"vimshottari_end": "2026-12-31",
|
||
"moon_sign": "Cancer",
|
||
"moon_house": 7,
|
||
})
|
||
assert check["ok"] is True
|
||
assert check["checks"]["vimshottari_start"]["status"] == "match"
|
||
|
||
|
||
def test_source_parsers_keep_family_split_gap():
|
||
registry = (ROOT / "frontend/src/lib/consultation-domain-registry.ts").read_text(encoding="utf-8")
|
||
methodology = (ROOT / "frontend/src/lib/consultation-methodology.ts").read_text(encoding="utf-8")
|
||
workflow = (ROOT / "frontend/src/mastra/consultation-workflow.ts").read_text(encoding="utf-8")
|
||
layers = parse_registry_layers(registry)
|
||
routes = parse_methodology_routes(methodology)
|
||
must_use = parse_must_use_layers(workflow)
|
||
assert "D12" in layers["family"] and "D7" in layers["family"]
|
||
assert "父母" not in "".join(layers["family"])
|
||
assert routes["family"] is None
|
||
# 原值:assert "family" not in must_use(调研时前端必用层没有 family)。
|
||
# 新值:family / parents / children 都有必用层,父母、子女有独立方法学条目(无命名清单)。
|
||
# 原因:TASK-consult-evidence-card-20260927 T2 按调研结论补齐;调研快照的「缺口」已修。
|
||
assert must_use["family"] == ["Formal Vargas D1–D60", "Narayana Dasha"]
|
||
assert must_use["parents"] == must_use["children"] == must_use["family"]
|
||
assert routes["parents"] is None and routes["children"] is None
|
||
assert "D12" in layers["parents"] and "D7" in layers["children"]
|
||
assert CARD_SPECS["parents"]["vargas"] == ["D12"]
|
||
assert CARD_SPECS["children"]["vargas"] == ["D7"]
|
||
|
||
|
||
def test_telemetry_spec_stores_no_user_text():
|
||
banned = {"question text", "answer text", "birth date", "name", "email", "user id"}
|
||
assert banned <= set(TELEMETRY_SPEC["does_not_store"])
|
||
stored = {field["name"] for field in TELEMETRY_SPEC["fields"]}
|
||
assert stored == {"domain", "card_version", "card_chars", "card_token_estimate", "cited_field_ids", "feedback"}
|
||
assert "matched substring" in TELEMETRY_SPEC["does_not_store"]
|
||
|
||
|
||
def test_single_domain_wrap_source_still_matches_projector():
|
||
# 原值:assert "return { ...consultations[0], ...plan, consultations };" in source
|
||
# (调研投影脚本模仿的「单领域整包再复制进 consultations」)。
|
||
# 新值:服务端完整上下文单领域不再复制整包;模型读 toModelEvidenceView(答题契约 + 数据卡)。
|
||
# 调研脚本 project_consult_model_context.ts 仍按旧形状复现「改前」体量,作为基线快照保留。
|
||
# 原因:TASK-consult-evidence-card-20260927 T3「去掉 consultations 整包重复」。
|
||
source = (ROOT / "frontend/src/mastra/consultation-tools.ts").read_text(encoding="utf-8")
|
||
assert "return { ...consultations[0], ...plan, consultations };" not in source
|
||
assert "return { ...consultations[0], ...plan };" in source
|
||
assert "export function toModelEvidenceView(" in source
|