research: consultation evidence-card inventory and draft
Independent Staging Quality Gate / validate (push) Failing after 6m40s
Independent Staging Quality Gate / publish (push) Skipped

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.
This commit is contained in:
jesse-ux
2026-09-27 01:27:57 +08:00
parent 3b67d8e945
commit d8d03b5a69
13 changed files with 8882 additions and 1 deletions
@@ -0,0 +1,811 @@
"""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
@@ -0,0 +1,263 @@
"""Run the consultation evidence-card inventory. Read-only toward the engine.
External VedAstro is not called. The product foreground path still joins a
background gateway; this process replaces that join with the engine's own
blocked payload so local timing is not a network wait. Status text from that
blocked payload is what the model sees when the gateway does not return.
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
import time
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[2]
SCRIPTS = ROOT / "scripts"
RESEARCH = Path(__file__).resolve().parent
for path in (ROOT, SCRIPTS, RESEARCH):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
from consult_evidence_card_lib import ( # noqa: E402
QUESTIONS,
card_dependencies,
category_report,
cut_evidence_card,
duplicate_report,
engine_facts,
estimate_tokens,
load_public_charts,
part_b_coverage,
source_matrix,
verbatim_check,
)
def _block_external_vedastro() -> None:
import vedastro_foreground as gateway
def start(self):
self.meta = {
"freshness": "research_not_called",
"external_vedastro": "not_called",
}
self.cached_gateway = gateway._blocked_foreground_vedastro(
reason="research_external_vedastro_not_called",
)
self.future = None
return self
gateway.ForegroundVedastroSession.start = start
def _workflow_body(chart: dict[str, Any], question: dict[str, str]) -> dict[str, Any]:
from consultation_plan_contract import PLAN_VERSION, plan_route_contract
domain = question["domain"]
contract = plan_route_contract(domain)
if contract is None:
raise RuntimeError(f"no plan contract for {domain}")
body = dict(chart["body"])
body.update({
"question": question["question"],
"question_text": question["question"],
"theme": list(contract.themes),
"plan_version": PLAN_VERSION,
"strict_workflow_route": domain,
"required_layers": list(contract.required_layers),
"claim_boundary": contract.claim_boundary,
"plan_depth": "standard",
"requested_domains": list(contract.requested_domains),
"timing_horizon": None,
"precision_boundary": "server_evidence_required",
"required_evidence_categories": list(contract.required_evidence_categories),
"surface": "api_web",
})
return body
def _run_workflow(body: dict[str, Any]) -> dict[str, Any]:
from jyotish_api_server import JyotishAPIHandler
handler = JyotishAPIHandler.__new__(JyotishAPIHandler)
return handler._compute_consultation_workflow(body)
def _project(jobs: list[dict[str, str]], summary_path: Path) -> list[dict[str, Any]]:
spec_path = summary_path.with_suffix(".jobs.json")
spec_path.write_text(json.dumps({"items": jobs}), encoding="utf-8")
tsx = ROOT / "frontend" / "node_modules" / "tsx" / "dist" / "cli.mjs"
env = os.environ.copy()
env["JYOTISHA_PROJECT_ROOT"] = str(ROOT)
command = [
"node",
str(tsx),
str(ROOT / "scripts" / "research" / "project_consult_model_context.ts"),
"project",
str(spec_path),
str(summary_path),
]
completed = subprocess.run(command, cwd=ROOT, env=env, capture_output=True, text=True)
if completed.returncode != 0:
raise RuntimeError(completed.stderr[-4000:] or completed.stdout[-4000:])
return json.loads(summary_path.read_text(encoding="utf-8"))
def _time_layers(chart_body: dict[str, Any]) -> dict[str, float]:
"""Time local layers on one birth payload. VedAstro stays blocked."""
from jyotish_api_server import (
JyotishAPIHandler,
_attach_local_consultation_layers,
_build_consultation_varga_spectrum,
_custom_research_dn_chart,
_planet_longitudes,
)
handler = JyotishAPIHandler.__new__(JyotishAPIHandler)
timings: dict[str, float] = {}
def elapsed(name: str, fn, *args, **kwargs):
started = time.perf_counter()
result = fn(*args, **kwargs)
timings[name] = round(time.perf_counter() - started, 4)
return result
chart = elapsed("compute_chart", handler._compute_chart, {
**chart_body,
"skip_vedastro_main_entry_overview": True,
})
planets = chart.get("planets") if isinstance(chart, dict) else {}
ascendant = chart.get("ascendant") if isinstance(chart, dict) else {}
elapsed("attach_local_layers", _attach_local_consultation_layers, handler, chart, chart_body, chart_body)
if isinstance(planets, dict) and isinstance(ascendant, dict) and planets and ascendant:
elapsed("varga_spectrum", _build_consultation_varga_spectrum, handler, planets, ascendant)
elapsed("shadbala", handler._compute_shadbala, {**chart_body, "planets": planets, "ascendant": ascendant})
elapsed("ashtakavarga", handler._compute_ashtakavarga, {"planets": planets, "ascendant": ascendant})
elapsed("functional_benefic_malefic", handler._functional_benefic_malefic_snapshot, planets, ascendant)
try:
module = __import__("divisional_charts_extended", fromlist=["DivisionalChartsCalculator"])
calc = module.DivisionalChartsCalculator()
planet_lons = _planet_longitudes(planets)
asc_lon = float(ascendant.get("lon"))
elapsed("one_research_division", _custom_research_dn_chart, calc, planet_lons, asc_lon, 3)
except Exception as exc:
timings["one_research_division_error"] = exc.__class__.__name__
return timings
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--out", default=str(ROOT / "docs" / "research" / "consult_evidence_card_inventory_2026_09_27.json"))
parser.add_argument("--cache", default="")
parser.add_argument("--only-chart", action="append", default=[])
parser.add_argument("--only-question", action="append", default=[])
parser.add_argument("--skip-timing", action="store_true")
args = parser.parse_args()
if os.environ.get("PYTHONHASHSEED") != "0":
raise SystemExit("Set PYTHONHASHSEED=0 before starting this process (ERR-111).")
_block_external_vedastro()
cache = Path(args.cache) if args.cache else Path(os.environ.get("TEMP", ".")) / "consult-evidence-card-20260927"
cache.mkdir(parents=True, exist_ok=True)
charts = load_public_charts(ROOT)
questions = list(QUESTIONS)
if args.only_chart:
charts = [chart for chart in charts if chart["id"] in args.only_chart]
if args.only_question:
questions = [item for item in questions if item["id"] in args.only_question]
runs = []
jobs = []
for chart in charts:
for question in questions:
run_id = f"{chart['id']}__{question['id']}"
workflow_path = cache / f"{run_id}.workflow.json"
started = time.perf_counter()
workflow = _run_workflow(_workflow_body(chart, question))
wall = round(time.perf_counter() - started, 3)
workflow_path.write_text(json.dumps(workflow, ensure_ascii=False), encoding="utf-8")
projected_path = cache / f"{run_id}.model.json"
jobs.append({
"id": run_id,
"domain": question["domain"],
"question": question["question"],
"workflow_path": str(workflow_path),
"out_path": str(projected_path),
})
runs.append({
"id": run_id,
"chart_id": chart["id"],
"chart_label": chart["label"],
"question_id": question["id"],
"domain": question["domain"],
"wall_seconds": wall,
"workflow_chars": workflow_path.stat().st_size,
"route": (workflow.get("routing") or {}).get("question_type"),
"focus_techniques": (workflow.get("routing") or {}).get("focus_techniques"),
"success": workflow.get("success"),
})
print(f"{run_id} wall={wall}s route={runs[-1]['route']}", flush=True)
summary_path = cache / "projection-summary.json"
projected = {item["id"]: item for item in _project(jobs, summary_path)}
inventory = []
for run, job in zip(runs, jobs):
model_context = json.loads(Path(job["out_path"]).read_text(encoding="utf-8"))
workflow = json.loads(Path(job["workflow_path"]).read_text(encoding="utf-8"))
categories = category_report(model_context)
card = cut_evidence_card(model_context, run["question_id"])
facts = engine_facts(workflow)
check = verbatim_check(card["card"], facts)
inventory.append({
**run,
"projection": projected.get(run["id"]),
"categories": categories,
"duplicates": duplicate_report(model_context),
"card_size": card["size"],
"card_gaps": card["gaps"],
"part_b": part_b_coverage(card["card"], run["question_id"]),
"verbatim": check,
"card_dependencies": card_dependencies(run["question_id"]),
"sample_card": card["card"] if run["chart_id"] == "steve_jobs" and run["question_id"] in {"parents", "career"} else None,
})
timings = {}
if not args.skip_timing:
for chart in charts:
print(f"timing {chart['id']}", flush=True)
try:
timings[chart["id"]] = _time_layers(chart["body"])
except Exception as exc:
timings[chart["id"]] = {"error": exc.__class__.__name__, "message": str(exc)[:300]}
payload = {
"baseline": "76924e3362c0a47d9d8de896c448c66055826f70",
"reference_date": "2026-09-27",
"ayanamsa": "raman",
"node_mode": "mean",
"pythonhashseed": "0",
"external_vedastro": "not_called",
"token_method": estimate_tokens("")["token_method"],
"charts": [
{"id": chart["id"], "label": chart["label"], "source": chart["source"], "case_id": chart["case_id"], "rodden_rating": chart["rodden_rating"]}
for chart in load_public_charts(ROOT)
],
"source_matrix": source_matrix(ROOT),
"runs": inventory,
"layer_timings_seconds": timings,
}
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"wrote {out}", flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,88 @@
"""Time local consultation layers on the real workflow path. No VedAstro network."""
from __future__ import annotations
import json
import os
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path[:0] = [str(ROOT), str(ROOT / "scripts"), str(ROOT / "scripts" / "research")]
from consult_evidence_card_lib import QUESTIONS, load_public_charts # noqa: E402
import consult_evidence_card_run as runner # noqa: E402
import jyotish_api_server as api # noqa: E402
def _wrap(bucket: dict[str, float], name: str, fn):
def inner(*args, **kwargs):
started = time.perf_counter()
try:
return fn(*args, **kwargs)
finally:
bucket[name] = round(bucket.get(name, 0.0) + time.perf_counter() - started, 4)
return inner
def main() -> int:
if os.environ.get("PYTHONHASHSEED") != "0":
raise SystemExit("Set PYTHONHASHSEED=0")
runner._block_external_vedastro()
charts = load_public_charts(ROOT)
question = QUESTIONS[0]
results = {}
originals = {
"compute_chart": api.JyotishAPIHandler._compute_chart,
"thematic": api.JyotishAPIHandler._compute_thematic_report,
"rectification": api.JyotishAPIHandler._compute_rectification_gate,
"attach": api._attach_local_consultation_layers,
"varga_spectrum": api._build_consultation_varga_spectrum,
"research_chart": api._custom_research_dn_chart,
"varga_full": api.JyotishAPIHandler._compute_varga_full,
}
for chart in charts:
bucket: dict[str, float] = {}
calls = {"research_chart": 0}
def counting_research(*args, **kwargs):
calls["research_chart"] += 1
return originals["research_chart"](*args, **kwargs)
api.JyotishAPIHandler._compute_chart = _wrap(bucket, "compute_chart", originals["compute_chart"])
api.JyotishAPIHandler._compute_thematic_report = _wrap(bucket, "thematic_report", originals["thematic"])
api.JyotishAPIHandler._compute_rectification_gate = _wrap(bucket, "rectification_gate", originals["rectification"])
api._attach_local_consultation_layers = _wrap(bucket, "attach_local_layers", originals["attach"])
api._build_consultation_varga_spectrum = _wrap(bucket, "varga_spectrum", originals["varga_spectrum"])
api._custom_research_dn_chart = _wrap(bucket, "research_dn_loop", counting_research)
api.JyotishAPIHandler._compute_varga_full = _wrap(bucket, "varga_full", originals["varga_full"])
started = time.perf_counter()
runner._run_workflow(runner._workflow_body(chart, question))
bucket["workflow_wall"] = round(time.perf_counter() - started, 4)
bucket["research_dn_calls"] = calls["research_chart"]
results[chart["id"]] = bucket
print(chart["id"], bucket, flush=True)
for name, fn in originals.items():
if name == "compute_chart":
api.JyotishAPIHandler._compute_chart = fn
elif name == "thematic":
api.JyotishAPIHandler._compute_thematic_report = fn
elif name == "rectification":
api.JyotishAPIHandler._compute_rectification_gate = fn
elif name == "attach":
api._attach_local_consultation_layers = fn
elif name == "varga_spectrum":
api._build_consultation_varga_spectrum = fn
elif name == "research_chart":
api._custom_research_dn_chart = fn
elif name == "varga_full":
api.JyotishAPIHandler._compute_varga_full = fn
out = ROOT / "docs" / "research" / "consult_evidence_card_timings_2026_09_27.json"
out.write_text(json.dumps(results, indent=2), encoding="utf-8")
print(f"wrote {out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,107 @@
/**
* Read-only projection of a consultation workflow payload into the text the
* answer model sees. Does not modify frontend runtime files.
*
* The single-domain wrap matches consultation-tools.ts toModelDomainPlanContext
* when consultations.length === 1 and omittedDomains.length === 0:
* return { ...consultations[0], ...plan, consultations };
*/
import { readFileSync, writeFileSync } from "node:fs";
import { createConsultationPlan } from "../../frontend/src/lib/consultation-plan.ts";
import { consultationMethodologyForDomains } from "../../frontend/src/lib/consultation-methodology.ts";
import type { ConsultationDomain } from "../../frontend/src/lib/consultation-domain-registry.ts";
import {
consultationWorkflowResponseSchema,
toAgentConsultationContext,
toModelOutput,
} from "../../frontend/src/mastra/consultation-workflow.ts";
type JsonRecord = Record<string, unknown>;
function readJson(path: string): JsonRecord {
return JSON.parse(readFileSync(path, "utf8")) as JsonRecord;
}
function planFields(domain: ConsultationDomain, question: string) {
const plan = createConsultationPlan({
userIntent: question,
theme: domain,
consultationMode: "verified_chart",
});
return {
plan_version: plan.plan_version,
depth: plan.depth,
requested_domains: plan.requestedDomains,
timing_horizon: plan.timingHorizon,
precision_boundary: plan.precisionBoundary,
required_evidence_categories: plan.requiredEvidenceCategories,
birthTimeMode: plan.birthTimeMode,
};
}
function projectOne(workflow: JsonRecord, domain: ConsultationDomain, question: string) {
const parsed = consultationWorkflowResponseSchema.safeParse(workflow);
const agentContext = toAgentConsultationContext(parsed.success ? parsed.data : workflow);
const consultationPlan = createConsultationPlan({
userIntent: question,
theme: domain,
consultationMode: "verified_chart",
});
const modelOutput = toModelOutput(agentContext, consultationPlan);
const consultation = { domain, ...modelOutput };
const methodology = consultationMethodologyForDomains([domain]);
const plan = {
success: agentContext.success === true,
domains: [domain],
omitted_domains: [] as ConsultationDomain[],
...(methodology ? { methodology } : {}),
};
const modelContext = { ...consultation, ...plan, consultations: [consultation] };
return {
schema_ok: parsed.success,
schema_error: parsed.success ? null : parsed.error.issues.slice(0, 8).map((issue) => ({
path: issue.path.join("."),
code: issue.code,
})),
methodology_sections: methodology?.sections.length ?? 0,
methodology_chars: methodology
? JSON.stringify(methodology).length
: 0,
model_context: modelContext,
};
}
const [mode, ...rest] = process.argv.slice(2);
if (mode === "plans") {
const spec = readJson(rest[0]);
const questions = spec.questions as Array<{ id: string; domain: ConsultationDomain; question: string }>;
const plans: JsonRecord = {};
for (const item of questions) {
plans[item.id] = planFields(item.domain, item.question);
}
writeFileSync(rest[1], JSON.stringify(plans));
} else if (mode === "project") {
const jobs = readJson(rest[0]) as {
items: Array<{ id: string; domain: ConsultationDomain; question: string; workflow_path: string; out_path: string }>;
};
const summary: JsonRecord[] = [];
for (const item of jobs.items) {
const workflow = readJson(item.workflow_path);
const projected = projectOne(workflow, item.domain, item.question);
const text = JSON.stringify(projected.model_context);
writeFileSync(item.out_path, text);
summary.push({
id: item.id,
schema_ok: projected.schema_ok,
schema_error: projected.schema_error,
methodology_sections: projected.methodology_sections,
methodology_chars: projected.methodology_chars,
model_chars: text.length,
});
}
writeFileSync(rest[1], JSON.stringify(summary));
} else {
process.stderr.write("usage: plans <spec.json> <out.json> | project <jobs.json> <summary.json>\n");
process.exit(2);
}