Files
Jyotisha/scripts/research/consult_evidence_card_lib.py
T
jesse-ux d8d03b5a69
Independent Staging Quality Gate / validate (push) Failing after 6m40s
Independent Staging Quality Gate / publish (push) Skipped
research: consultation evidence-card inventory and draft
Measure the model-visible consultation payload on three public charts, draft per-domain cards, and record the Narayana/pratyantar projection gap as BUG-1054. No runtime behavior change.
2026-09-27 01:27:57 +08:00

812 lines
31 KiB
Python

"""Pure helpers for the consultation evidence-card research.
No network, no engine mutation. Classification partitions a JSON value so the
five category sizes sum to the canonical serialization length.
"""
from __future__ import annotations
import json
import re
from collections import Counter
from pathlib import Path
from typing import Any
CATEGORIES = ("core", "status", "not_applicable", "research", "western")
TOKEN_CHARS_PER_TOKEN = 3.5
TOKEN_METHOD = (
"chars/3.5. The 2026-09-27 diagnosis measured about 144000 projected "
"characters as about 40000 tokens (3.6 chars/token). This estimate uses "
"3.5 so the research tables stay comparable. It is not a vendor tokenizer."
)
REFERENCE_DATE = "2026-09-27"
AYANAMSA = "raman"
NODE_MODE = "mean"
QUESTIONS = (
{"id": "career", "domain": "career", "question": "未来一年,事业和收入该关注什么?"},
{"id": "marriage", "domain": "marriage", "question": "我的关系模式是什么?"},
{"id": "wealth", "domain": "wealth", "question": "我的财富增长方式和风险点是什么?"},
{"id": "health", "domain": "health", "question": "我近期的身心压力模式和调节重点是什么?"},
{"id": "education", "domain": "education", "question": "我的学习优势、瓶颈和进阶方向是什么?"},
{"id": "migration", "domain": "migration", "question": "迁居、置业或海外发展更适合怎样规划?"},
{"id": "family", "domain": "family", "question": "我的家庭关系与责任边界该如何理解?"},
{"id": "general", "domain": "general", "question": "请综合说明我当前最值得关注的主题。"},
{"id": "parents", "domain": "family", "question": "我和父母关系如何"},
{"id": "children", "domain": "family", "question": "我和子女的关系如何"},
)
CHART_SOURCES = (
{
"id": "steve_jobs",
"label": "Steve Jobs",
"path": "references/real_case_calibration/minute_rectification_development_v1.json",
"case_id": "steve_jobs_1955_development",
},
{
"id": "barack_obama",
"label": "Barack Obama",
"path": "references/real_case_calibration/minute_rectification_holdout_v4.json",
"case_id": "barack_obama_1961_aa_v4_holdout",
},
{
"id": "elizabeth_taylor",
"label": "Elizabeth Taylor",
"path": "references/real_case_calibration/minute_rectification_holdout_v4.json",
"case_id": "elizabeth_taylor_1932_aa_v4_holdout",
},
)
STATUS_TECHNIQUES = {
"mevg / global web evidence",
"real case calibration",
"vedastro main entry overview",
"vedastro cloud state",
"external oracle progress",
"interpretation source pack",
"module execution audit",
"exact cusp oracle",
"blind technical mode",
"timing precision gate",
"evidence packet",
"shadbala boundary",
}
RESEARCH_TECHNIQUES = {
"research d-n through d60",
"extended vargas d81/d108/d144",
"ashtottari dasha",
}
NOT_APPLICABLE_TECHNIQUES = {
"prashna chart",
"ashtakoota matching",
"adhana / niseka",
"muhurta election",
}
WESTERN_PREFIX = "western"
STATUS_KEYS = {
"methodology",
"presentation",
"rectification",
"answer_policy",
"user_facing_limitation",
"hard_blockers",
"missing_route_layers",
"available_layers",
"must_use_layers",
"technique_truth",
"reference_transparency",
"shadbala_boundary",
"vedastro_cross_check",
"vedastro_gateway",
"commercial_evidence_status",
"success",
"omitted_domains",
"status",
"packet_version",
"schema_error",
}
# Domain card draft. Values are copied from the projection; nothing is rewritten.
CARD_SPECS: dict[str, dict[str, Any]] = {
"career": {
"label": "事业",
"vargas": ["D10"],
"houses": [10],
"arudha": ["A10"],
"karakas": ["AmK"],
"planets": [],
"yogas": True,
"transits": True,
"ashtakavarga": False,
"shadbala": True,
},
"marriage": {
"label": "婚恋",
"vargas": ["D9"],
"houses": [7],
"arudha": ["UL", "A7"],
"karakas": ["DK"],
"planets": ["venus", "jupiter"],
"yogas": False,
"transits": True,
"ashtakavarga": False,
"shadbala": False,
},
"wealth": {
"label": "财富",
"vargas": ["D2", "D11"],
"houses": [2, 11, 9, 5],
"arudha": [],
"karakas": [],
"planets": [],
"yogas": True,
"transits": False,
"ashtakavarga": True,
"shadbala": True,
},
"health": {
"label": "健康",
"vargas": ["D6", "D8", "D30"],
"houses": [6, 8],
"arudha": [],
"karakas": [],
"planets": [],
"yogas": False,
"transits": True,
"ashtakavarga": False,
"shadbala": True,
},
"education": {
"label": "学习",
"vargas": ["D5", "D24"],
"houses": [5, 9],
"arudha": [],
"karakas": [],
"planets": ["mercury", "jupiter"],
"yogas": False,
"transits": False,
"ashtakavarga": False,
"shadbala": False,
},
"migration": {
"label": "迁居",
"vargas": ["D4", "D12"],
"houses": [4, 12],
"arudha": [],
"karakas": [],
"planets": [],
"yogas": False,
"transits": False,
"ashtakavarga": False,
"shadbala": False,
},
"family": {
"label": "家庭(现路由,父母与子女混在一起)",
"vargas": ["D7", "D12"],
"houses": [4, 5, 9],
"arudha": [],
"karakas": [],
"planets": [],
"yogas": False,
"transits": False,
"ashtakavarga": False,
"shadbala": False,
},
"parents": {
"label": "父母",
"vargas": ["D12"],
"houses": [4, 9],
"arudha": [],
"karakas": [],
"planets": ["sun", "moon"],
"yogas": False,
"transits": False,
"ashtakavarga": False,
"shadbala": False,
"basis": [
"skills/jyotish-vedic-astrology/references/strict-workflow-router.md shared baseline: D12 for family/ancestral themes",
"references/birth-time-rectification-decision-tree.md section 4: 父母/家族 -> D12",
],
},
"children": {
"label": "子女",
"vargas": ["D7"],
"houses": [5],
"arudha": [],
"karakas": ["PK"],
"planets": ["jupiter"],
"yogas": False,
"transits": False,
"ashtakavarga": False,
"shadbala": False,
"basis": [
"skills/jyotish-vedic-astrology/references/strict-workflow-router.md shared baseline: D7 for children",
"references/birth-time-rectification-decision-tree.md section 4: 子女/生育 -> D7",
],
},
"general": {
"label": "综合",
"vargas": ["D9", "D10", "D2"],
"houses": [1, 10, 7, 2],
"arudha": ["A10"],
"karakas": [],
"planets": [],
"yogas": True,
"transits": True,
"ashtakavarga": True,
"shadbala": True,
},
}
PART_B_BASE = (
"functional_benefic_malefic",
"vimshottari_md_ad_pd",
"narayana_current",
"ascendant_degree",
)
def dumps(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
def estimate_tokens(text: str) -> dict[str, Any]:
chars = len(text)
return {
"chars": chars,
"token_estimate": int(round(chars / TOKEN_CHARS_PER_TOKEN)),
"token_method": TOKEN_METHOD,
}
def _norm(value: str) -> str:
return re.sub(r"\s+", " ", value).strip().lower()
def classify_audit_row(row: dict[str, Any]) -> str:
technique = _norm(str(row.get("technique") or ""))
system = _norm(str(row.get("system") or ""))
status = _norm(str(row.get("status") or ""))
if system == "western" or technique.startswith(WESTERN_PREFIX):
return "western"
if technique in RESEARCH_TECHNIQUES:
return "research"
if status == "not_applicable" or technique in NOT_APPLICABLE_TECHNIQUES:
return "not_applicable"
if technique in STATUS_TECHNIQUES or "vedastro" in technique or "mevg" in technique:
return "status"
return "core"
def classify(path: tuple[str, ...], value: Any) -> str:
joined = ".".join(path).lower()
if "western_spectrum" in joined or joined.startswith("western"):
return "western"
if "research_dn" in joined or "extended" in joined and "varga" in joined:
return "research"
if isinstance(value, dict) and "technique" in value and "status" in value and "technique_audit" in joined:
return classify_audit_row(value)
leaf = path[-1].lower() if path else ""
if leaf in STATUS_KEYS or leaf.endswith("_boundary") or leaf.endswith("_status"):
return "status"
if isinstance(value, str) and _norm(value) == "not_applicable":
return "not_applicable"
return "core"
def _whole(path: tuple[str, ...], value: Any) -> str | None:
joined = ".".join(path).lower()
if joined.endswith("western_spectrum"):
return "western"
if joined.endswith("research_dn") or joined.endswith("extended") and "varga_spectrum" in joined:
return "research"
if joined.endswith("methodology") or joined.endswith("presentation") or joined.endswith("rectification"):
return "status"
if (
isinstance(value, dict)
and "technique" in value
and "status" in value
and any(part == "technique_audit_table" for part in path)
):
return classify_audit_row(value)
return None
def _dominant(sizes: Counter[str]) -> str:
if not sizes:
return "status"
return max(CATEGORIES, key=lambda name: (sizes.get(name, 0), -CATEGORIES.index(name)))
def account(value: Any, path: tuple[str, ...] = ()) -> Counter[str]:
"""Partition dumps(value) across the five categories. Sum equals len(dumps(value))."""
whole = _whole(path, value)
if whole is not None:
return Counter({whole: len(dumps(value))})
if isinstance(value, dict):
sizes: Counter[str] = Counter()
if not value:
sizes[classify(path, value)] = 2
return sizes
items = list(value.items())
for index, (key, child) in enumerate(items):
child_path = path + (str(key),)
child_sizes = account(child, child_path)
sizes.update(child_sizes)
overhead = len(dumps(str(key))) + 1
if index:
overhead += 1
sizes[_dominant(child_sizes)] += overhead
sizes[_dominant(sizes)] += 2
return sizes
if isinstance(value, list):
sizes = Counter()
if not value:
sizes[classify(path, value)] = 2
return sizes
for index, child in enumerate(value):
child_sizes = account(child, path + (str(index),))
sizes.update(child_sizes)
if index:
sizes[_dominant(child_sizes)] += 1
sizes[_dominant(sizes)] += 2
return sizes
return Counter({classify(path, value): len(dumps(value))})
def category_report(value: Any) -> dict[str, Any]:
sizes = account(value)
total = len(dumps(value))
covered = sum(sizes[name] for name in CATEGORIES)
return {
"categories": {name: int(sizes.get(name, 0)) for name in CATEGORIES},
"total_chars": total,
"category_sum": covered,
"sum_error_ratio": 0.0 if total == 0 else abs(covered - total) / total,
"tokens": estimate_tokens(dumps(value)),
}
def duplicate_report(model_context: dict[str, Any]) -> dict[str, Any]:
consultations = model_context.get("consultations")
first = consultations[0] if isinstance(consultations, list) and consultations else None
spread_keys = [
key for key in (first or {})
if key in model_context and key != "consultations"
]
matching = [
key for key in spread_keys
if dumps(model_context.get(key)) == dumps(first.get(key))
]
named_copies: dict[str, int] = {}
def walk(node: Any, path: tuple[str, ...] = ()) -> None:
if isinstance(node, dict):
for key, child in node.items():
leaf = str(key)
if leaf in {"varga_spectrum", "western_spectrum", "technique_audit_table", "methodology"}:
named_copies[leaf] = named_copies.get(leaf, 0) + 1
walk(child, path + (leaf,))
elif isinstance(node, list):
for index, child in enumerate(node):
walk(child, path + (str(index),))
walk(model_context)
consultation_chars = len(dumps(consultations)) if consultations is not None else 0
return {
"consultation_copies": 1 + (1 if isinstance(first, dict) else 0),
"spread_keys_matching_consultations_0": matching,
"consultations_chars": consultation_chars,
"named_subtree_copies": named_copies,
}
def load_public_charts(root: Path) -> list[dict[str, Any]]:
charts = []
for spec in CHART_SOURCES:
payload = json.loads((root / spec["path"]).read_text(encoding="utf-8"))
case = next(item for item in payload["cases"] if item["case_id"] == spec["case_id"])
birth = case["birth"]
year, month, day = (int(part) for part in birth["date"].split("-"))
hour, minute = (int(part) for part in birth["time"].split(":")[:2])
charts.append({
"id": spec["id"],
"label": spec["label"],
"source": spec["path"],
"case_id": spec["case_id"],
"rodden_rating": (birth.get("source") or {}).get("rodden_rating"),
"place": birth.get("place"),
"body": {
"year": year,
"month": month,
"day": day,
"hour": hour,
"minute": minute,
"second": 0,
"lat": birth["latitude"],
"lon": birth["longitude"],
"tz": birth["timezone_offset"],
"city": birth.get("place") or spec["label"],
"ayanamsa": AYANAMSA,
"node_mode": NODE_MODE,
"today": REFERENCE_DATE,
"entry_mode": "direct_chart",
"defer_optional_external_evidence": True,
"declared_accuracy": "minute",
"birth_time_accuracy": "confirmed",
},
})
return charts
def _dig(value: Any, *keys: str) -> Any:
current = value
for key in keys:
if not isinstance(current, dict) or key not in current:
return None
current = current[key]
return current
def claim_evidence(model_context: dict[str, Any], category: str) -> dict[str, Any]:
cards = model_context.get("claim_cards")
if not isinstance(cards, list):
return {}
for card in cards:
if isinstance(card, dict) and card.get("category") == category and isinstance(card.get("evidence"), dict):
return card["evidence"]
return {}
def _house_entry(houses: Any, number: int) -> Any:
if isinstance(houses, dict):
for key in (str(number), f"house_{number}", f"h{number}"):
if key in houses:
return houses[key]
for value in houses.values():
if isinstance(value, dict) and value.get("number") == number:
return value
if isinstance(houses, list):
for value in houses:
if isinstance(value, dict) and value.get("number") == number:
return value
if 1 <= number <= len(houses):
return houses[number - 1]
return None
def _pick(mapping: Any, names: list[str]) -> dict[str, Any]:
if not isinstance(mapping, dict):
return {}
lowered = {str(key).lower(): key for key in mapping}
picked = {}
for name in names:
key = lowered.get(name.lower())
if key is not None:
picked[str(key)] = mapping[key]
return picked
def cut_evidence_card(model_context: dict[str, Any], question_id: str) -> dict[str, Any]:
"""Copy card fields from the projected model context. Missing paths stay gaps."""
spec = CARD_SPECS[question_id]
natal = claim_evidence(model_context, "natal_foundation")
timing = claim_evidence(model_context, "timing")
spectrum = natal.get("varga_spectrum") if isinstance(natal.get("varga_spectrum"), dict) else {}
formal = spectrum.get("formal") if isinstance(spectrum.get("formal"), dict) else {}
houses = natal.get("houses")
planets = natal.get("planets") if isinstance(natal.get("planets"), dict) else {}
functional = natal.get("functional_benefic_malefic")
dasha = timing.get("dasha_sub_periods") if isinstance(timing.get("dasha_sub_periods"), dict) else timing.get("dasha")
narayana = timing.get("narayana_dasha")
gaps = []
vargas = {}
for code in spec["vargas"]:
chart = formal.get(code)
if chart is None:
gaps.append(f"varga:{code}")
else:
vargas[code] = chart
house_rows = {}
for number in spec["houses"]:
row = _house_entry(houses, number)
if row is None:
gaps.append(f"house:{number}")
else:
house_rows[str(number)] = row
picked_planets = _pick(planets, spec["planets"])
for name in spec["planets"]:
if name.lower() not in {key.lower() for key in picked_planets}:
gaps.append(f"planet:{name}")
card = {
"card_id": question_id,
"label": spec["label"],
"question": model_context.get("question"),
"route": model_context.get("route"),
"base": {
"ascendant": natal.get("ascendant"),
"houses": houses,
"planet_placements": _planet_placements(planets),
"functional_benefic_malefic": functional,
"vimshottari": dasha,
"narayana_dasha": narayana,
},
"domain": {
"vargas": vargas,
"houses": house_rows,
"planets": picked_planets,
"arudha": _pick(natal.get("arudha_padas"), spec["arudha"]),
"karakas": _pick(_dig(natal, "jaimini", "chara_karakas") or natal.get("chara_karakas"), spec["karakas"]),
"yogas": natal.get("yogas") if spec["yogas"] else None,
"transits": timing.get("transits") if spec["transits"] else None,
"ashtakavarga": natal.get("ashtakavarga") if spec["ashtakavarga"] else None,
"shadbala": natal.get("shadbala") if spec["shadbala"] else None,
},
"gaps": gaps,
"basis": spec.get("basis", []),
}
if card["base"]["ascendant"] is None:
gaps.append("ascendant")
if functional is None:
gaps.append("functional_benefic_malefic")
if dasha is None:
gaps.append("vimshottari")
if narayana is None:
gaps.append("narayana_dasha")
text = dumps(card)
return {"card": card, "text": text, "size": estimate_tokens(text), "gaps": gaps}
def _ci_get(mapping: Any, name: str) -> Any:
if not isinstance(mapping, dict):
return None
for key, value in mapping.items():
if str(key).lower() == name.lower():
return value
return None
def _planet_placements(planets: Any) -> dict[str, Any]:
if not isinstance(planets, dict):
return {}
placed = {}
for name, row in planets.items():
if isinstance(row, dict):
placed[str(name)] = {
key: row[key] for key in ("sign", "house") if key in row
}
elif isinstance(row, str):
placed[str(name)] = {"sign": row}
return placed
def engine_facts(workflow: dict[str, Any]) -> dict[str, Any]:
chart = workflow.get("chart") if isinstance(workflow.get("chart"), dict) else {}
modules = chart.get("modules") if isinstance(chart.get("modules"), dict) else {}
ascendant = chart.get("ascendant") if isinstance(chart.get("ascendant"), dict) else {}
planets = chart.get("planets") if isinstance(chart.get("planets"), dict) else {}
moon = _ci_get(planets, "moon")
moon = moon if isinstance(moon, dict) else {}
sub = modules.get("dasha_sub_periods") if isinstance(modules.get("dasha_sub_periods"), dict) else {}
current = sub.get("current") if isinstance(sub.get("current"), dict) else {}
mahadasha = current.get("mahadasha") if isinstance(current.get("mahadasha"), dict) else {}
antardasha = current.get("antardasha") if isinstance(current.get("antardasha"), dict) else {}
pratyantar = sub.get("pratyantar_dasha_timeline") if isinstance(sub.get("pratyantar_dasha_timeline"), dict) else {}
pratyantar_current = pratyantar.get("current") if isinstance(pratyantar.get("current"), dict) else {}
narayana = modules.get("narayana_dasha") if isinstance(modules.get("narayana_dasha"), dict) else {}
narayana_current = narayana.get("current_dasha") if isinstance(narayana.get("current_dasha"), dict) else {}
narayana_md = narayana_current.get("md") if isinstance(narayana_current.get("md"), dict) else {}
spectrum = modules.get("varga_spectrum") if isinstance(modules.get("varga_spectrum"), dict) else {}
formal = spectrum.get("formal") if isinstance(spectrum.get("formal"), dict) else {}
d12 = formal.get("D12") if isinstance(formal.get("D12"), dict) else {}
return {
"ascendant_sign": ascendant.get("sign"),
"moon_sign": moon.get("sign"),
"moon_house": moon.get("house"),
"vimshottari_lord": mahadasha.get("lord"),
"vimshottari_start": mahadasha.get("start"),
"vimshottari_end": mahadasha.get("end"),
"antardasha_start": antardasha.get("start"),
"antardasha_end": antardasha.get("end"),
"pratyantar_start": pratyantar_current.get("start"),
"pratyantar_end": pratyantar_current.get("end"),
"narayana_sign": narayana_md.get("sign"),
"d12_lagna": d12.get("lagna") if isinstance(d12.get("lagna"), str) else None,
"d12_moon": _ci_get(d12.get("planets"), "moon") if isinstance(d12.get("planets"), dict) else None,
}
def card_facts(card: dict[str, Any]) -> dict[str, Any]:
base = card.get("base") if isinstance(card.get("base"), dict) else {}
domain = card.get("domain") if isinstance(card.get("domain"), dict) else {}
ascendant = base.get("ascendant") if isinstance(base.get("ascendant"), dict) else {}
moon = _ci_get(base.get("planet_placements"), "moon")
if not isinstance(moon, dict):
moon = _ci_get(domain.get("planets"), "moon")
moon = moon if isinstance(moon, dict) else {}
sub = base.get("vimshottari") if isinstance(base.get("vimshottari"), dict) else {}
current = sub.get("current") if isinstance(sub.get("current"), dict) else {}
mahadasha = current.get("mahadasha") if isinstance(current.get("mahadasha"), dict) else {}
antardasha = current.get("antardasha") if isinstance(current.get("antardasha"), dict) else {}
pratyantar = sub.get("pratyantar_dasha_timeline") if isinstance(sub.get("pratyantar_dasha_timeline"), dict) else {}
pratyantar_current = pratyantar.get("current") if isinstance(pratyantar.get("current"), dict) else {}
narayana = base.get("narayana_dasha") if isinstance(base.get("narayana_dasha"), dict) else {}
narayana_current = narayana.get("current_dasha") if isinstance(narayana.get("current_dasha"), dict) else {}
narayana_md = narayana_current.get("md") if isinstance(narayana_current.get("md"), dict) else {}
d12 = (domain.get("vargas") or {}).get("D12") if isinstance(domain.get("vargas"), dict) else None
d12 = d12 if isinstance(d12, dict) else {}
return {
"ascendant_sign": ascendant.get("sign"),
"moon_sign": moon.get("sign"),
"moon_house": moon.get("house"),
"vimshottari_lord": mahadasha.get("lord"),
"vimshottari_start": mahadasha.get("start"),
"vimshottari_end": mahadasha.get("end"),
"antardasha_start": antardasha.get("start"),
"antardasha_end": antardasha.get("end"),
"pratyantar_start": pratyantar_current.get("start"),
"pratyantar_end": pratyantar_current.get("end"),
"narayana_sign": narayana_md.get("sign"),
"d12_lagna": d12.get("lagna"),
"d12_moon": _ci_get(d12.get("planets"), "moon"),
}
def verbatim_check(card: dict[str, Any], facts: dict[str, Any]) -> dict[str, Any]:
actual = card_facts(card)
has_d12 = isinstance((card.get("domain") or {}).get("vargas"), dict) and "D12" in card["domain"]["vargas"]
checks = {}
for key, expected in facts.items():
if key.startswith("d12_") and not has_d12:
checks[key] = {"status": "not_on_card", "expected": expected, "actual": None}
continue
got = actual.get(key)
if expected is None or expected == "":
checks[key] = {"status": "engine_missing", "expected": None, "actual": got}
elif got == expected:
checks[key] = {"status": "match", "expected": expected, "actual": got}
else:
checks[key] = {"status": "mismatch", "expected": expected, "actual": got}
mismatched = [key for key, row in checks.items() if row["status"] == "mismatch"]
return {"ok": not mismatched, "checks": checks, "mismatched": mismatched}
def parse_registry_layers(source: str) -> dict[str, list[str]]:
found = {}
for match in re.finditer(r'id: "(\w+)".*?requiredLayers: \[([^\]]*)\]', source):
layers = re.findall(r'"([^"]+)"', match.group(2))
found[match.group(1)] = layers
return found
def parse_methodology_routes(source: str) -> dict[str, str | None]:
found = {}
for match in re.finditer(r'(\w+): \{ strictRoute: (?:"([^"]+)"|null)', source):
found[match.group(1)] = match.group(2)
return found
def parse_must_use_layers(source: str) -> dict[str, list[str]]:
block = re.search(r"DOMAIN_MUST_USE_LAYERS[\s\S]*?};", source)
if not block:
return {}
found = {}
for match in re.finditer(r'(\w+): \[([^\]]*)\]', block.group(0)):
found[match.group(1)] = re.findall(r'"([^"]+)"', match.group(2))
return found
def source_matrix(root: Path) -> dict[str, Any]:
registry = parse_registry_layers((root / "frontend/src/lib/consultation-domain-registry.ts").read_text(encoding="utf-8"))
methodology = parse_methodology_routes((root / "frontend/src/lib/consultation-methodology.ts").read_text(encoding="utf-8"))
must_use = parse_must_use_layers((root / "frontend/src/mastra/consultation-workflow.ts").read_text(encoding="utf-8"))
from unified_consultation_orchestrator import UnifiedConsultationOrchestrator
python_routes = {
name: list(route.focus_techniques)
for name, route in UnifiedConsultationOrchestrator._ROUTE_DEFINITIONS.items()
}
domains = sorted(set(registry) | set(methodology) | set(python_routes) | set(CARD_SPECS))
rows = []
for domain in domains:
card_id = domain if domain in CARD_SPECS else None
rows.append({
"domain": domain,
"registry_required_layers": registry.get(domain, []),
"methodology_strict_route": methodology.get(domain),
"ts_must_use_layers": must_use.get(domain, []),
"python_focus_techniques": python_routes.get(domain, []),
"card_vargas": CARD_SPECS.get(card_id, {}).get("vargas", []) if card_id else [],
"parents_children_split": domain == "family",
})
return {"domains": rows, "skill_citations": {
"d12_family": "references/strict-workflow-router.md: D12 for family/ancestral themes",
"d7_children": "references/strict-workflow-router.md: D7 for children",
"decision_tree_parents": "references/birth-time-rectification-decision-tree.md: 父母/家族 -> D12",
"decision_tree_children": "references/birth-time-rectification-decision-tree.md: 子女/生育 -> D7",
}}
def part_b_coverage(card: dict[str, Any], question_id: str) -> dict[str, Any]:
base = card.get("base") if isinstance(card.get("base"), dict) else {}
domain = card.get("domain") if isinstance(card.get("domain"), dict) else {}
spec = CARD_SPECS[question_id]
present = {
"functional_benefic_malefic": base.get("functional_benefic_malefic") is not None,
"vimshottari": base.get("vimshottari") is not None,
"narayana": base.get("narayana_dasha") is not None,
"ascendant_raw": base.get("ascendant") is not None,
"domain_vargas": sorted(domain.get("vargas") or {}),
"expected_vargas": spec["vargas"],
}
missing_vargas = [code for code in spec["vargas"] if code not in (domain.get("vargas") or {})]
return {
"dual_dasha": present["vimshottari"] and present["narayana"],
"domain_divisional": not missing_vargas,
"functional_benefic_malefic": present["functional_benefic_malefic"],
"raw_data": present["ascendant_raw"] and present["vimshottari"],
"mevg": "kept_backstage",
"real_case_calibration": "kept_backstage",
"missing_vargas": missing_vargas,
"present": present,
}
TELEMETRY_SPEC = {
"stores": "numbers, enums, and field names only",
"does_not_store": [
"question text",
"answer text",
"birth date",
"birth time",
"birth place",
"name",
"email",
"user id",
"session id",
"matched substring",
],
"fields": [
{"name": "domain", "type": "enum", "values": [item["id"] for item in QUESTIONS]},
{"name": "card_version", "type": "enum", "values": ["evidence-card-draft-20260927"]},
{"name": "card_chars", "type": "int"},
{"name": "card_token_estimate", "type": "int"},
{"name": "cited_field_ids", "type": "enum[]", "note": "ids from the card schema, not free text"},
{"name": "feedback", "type": "enum", "values": ["up", "down", "none"]},
],
"citation_method": {
"rule": "A field is cited when its scalar value appears in the answer and the value is an ISO date, a degree token, or a compound planet-in-sign phrase of at least 8 characters. Bare planet or sign names are not counted.",
"false_positive": "A date or degree that also appears in the question, or a repeated stock phrase, can be counted without the model using that field.",
"false_negative": "A paraphrase that names the period without copying the date is not counted.",
"stored": "Only the field id. The matched text is discarded.",
},
"aggregates": [
"count of answers by domain and card_version",
"fields whose cited rate stays under 5 percent after 30 answers in that domain",
"down rate by domain and card_version",
],
}
def card_dependencies(question_id: str) -> list[str]:
spec = CARD_SPECS[question_id]
modules = ["compute_chart", "functional_benefic_malefic", "dasha_sub_periods", "narayana_dasha"]
if spec["vargas"]:
modules.append("varga_requested_only")
if spec["arudha"] or spec["karakas"]:
modules.append("arudha_padas")
if spec["karakas"]:
modules.append("chara_karakas")
if spec["transits"]:
modules.append("transits")
if spec["ashtakavarga"]:
modules.append("ashtakavarga")
if spec["shadbala"]:
modules.append("shadbala")
if spec["yogas"]:
modules.append("yogas")
return modules