Add domain invocation governance contracts

This commit is contained in:
732642856
2026-07-02 15:40:34 +08:00
parent a3d2e0acf9
commit 0e3adb5a13
8 changed files with 515 additions and 0 deletions
+29
View File
@@ -1274,6 +1274,7 @@ function renderAIPromptPackPanel(cd) {
</div>
` : ''}
${renderStrictWorkflowContractPanel(vedastroOfficial)}
${renderInterpretationSourceGovernancePanel(evidence)}
</div>
</div>
<div class="ai-prompt-pack-foot">
@@ -1412,6 +1413,34 @@ function renderStrictWorkflowContractCard(route, contract = {}, isPrimary = fals
`;
}
function renderInterpretationSourceGovernancePanel(evidence = {}) {
const sourcePack = evidence.interpretation_source_pack || {};
const boundary = evidence.prediction_boundary_contract || {};
const mevg = evidence.mevg_collection_queue || {};
const cases = evidence.real_case_calibration_layer || {};
const debt = evidence.technical_debt_contract || {};
const remaining = evidence.remaining_priority1_batch_queue || {};
const coreRefs = sourcePack.core_rule_source_refs || [];
const referenceOnly = sourcePack.reference_only_source_refs || [];
const blockedPolicy = boundary.confidence_boundary?.unverified_claim_policy || 'downgrade_or_block';
return `
<div class="ai-prompt-pack-contracts">
<h4>Source Governance</h4>
<div class="ai-prompt-pack-contract-grid">
<span>core sources: ${escapeHtml(coreRefs.length ? String(coreRefs.length) : 'none')}</span>
<span>reference-only: ${escapeHtml(referenceOnly.length ? String(referenceOnly.length) : 'none')}</span>
<span>blocked non-runtime: duplicate / obsolete / quarantine</span>
<span>confidence downgrade: ${escapeHtml(blockedPolicy)}</span>
<span>MEVG queue: ${escapeHtml(mevg.status || 'blocked')}</span>
<span>case calibration: ${escapeHtml(cases.status || 'blocked')}</span>
<span>Narayana debt: ${escapeHtml(debt.narayana?.status || 'unknown')}</span>
<span>Tajika debt: ${escapeHtml(debt.tajika?.status || 'unknown')}</span>
<span>next batches: ${escapeHtml((remaining.next_batches || []).join(' / ') || 'none')}</span>
</div>
</div>
`;
}
function renderVedAstroOverviewPromptCard(overview = {}) {
if (!overview || typeof overview !== 'object') return '';
const status = overview.status || 'blocked';
+159
View File
@@ -142,6 +142,29 @@ PROMOTE_BATCH2_TOPIC_SOURCE_REFS = [
"references/condition-dasha-complete.md",
]
DASHA_TIMING_SOURCE_REFS = [
"references/vimshottari_dasha_guide.md",
"references/pratyantar-calculation-guide.md",
"references/condition-dasha-complete.md",
]
VARGA_STRENGTH_SOURCE_REFS = [
"references/divisional-chart-deep-reading.md",
"references/shadbala-complete-methodology.md",
"references/ashtakavarga-complete-system.md",
]
ANNUAL_SPECIAL_SOURCE_REFS = [
"references/tajika-yoga-complete-guide.md",
"references/jaimini-complete-system.md",
"references/kp-astrology-complete-system.md",
]
MODIFIER_OBSTACLE_SOURCE_REFS = [
"references/argala-complete-guide.md",
"references/badhaka-obstacle-planet-guide.md",
]
REFERENCE_ONLY_CONFLICT_SOURCE_REFS = [
"references/dasa-convergence-methodology.md",
"references/multi-dasha-convergence-protocol.md",
@@ -159,6 +182,42 @@ BLOCKED_NON_RUNTIME_SOURCE_REFS = [
"references/consultation-case-library.md",
]
REMAINING_PRIORITY1_BATCH_QUEUE = [
"real_case_studies_batch1",
"rishi_ai_mcp_batch1",
"vedic_astro_skills_batch1",
"references_batch2",
]
def _domain_invocation_layers() -> Dict[str, Any]:
return {
"dasha_timing": {
"status": "available",
"source_refs": DASHA_TIMING_SOURCE_REFS,
"required_in_routes": ["career", "relationship", "finance"],
"contract": "timing must cite Vimshottari/Narayana cross-check and sub-period boundaries when used.",
},
"varga_strength": {
"status": "available",
"source_refs": VARGA_STRENGTH_SOURCE_REFS,
"required_in_routes": ["career", "relationship", "finance"],
"contract": "domain conclusions must include relevant varga and strength/ashtakavarga boundaries.",
},
"annual_special": {
"status": "available",
"source_refs": ANNUAL_SPECIAL_SOURCE_REFS,
"required_in_routes": ["career", "relationship", "finance"],
"contract": "annual/special systems are supporting layers until oracle parity is closed.",
},
"modifier_obstacle": {
"status": "available",
"source_refs": MODIFIER_OBSTACLE_SOURCE_REFS,
"required_in_routes": ["career", "relationship", "finance"],
"contract": "Argala and Badhaka are modifiers/obstacle indicators, not standalone event guarantees.",
},
}
def _build_interpretation_source_inventory(source_refs: List[str]) -> Dict[str, Any]:
primary_truth = [
@@ -440,6 +499,12 @@ def _existing_interpretation_source_pack() -> Dict[str, Any]:
"promotion_status": "not_truth_source",
"boundary": "Duplicate, obsolete, and quarantined files are deliberately excluded from runtime source_refs.",
},
"domain_invocation_layers": _domain_invocation_layers(),
"remaining_priority1_batch_queue": {
"status": "queued",
"next_batches": REMAINING_PRIORITY1_BATCH_QUEUE,
"boundary": "Future batches remain audit-only until classified and tested.",
},
"frontend_interpretation_layer": {
"status": "available" if all(_repo_relative_exists(path) for path in frontend_interpretation_paths) else "partial",
"source_refs": frontend_interpretation_paths,
@@ -2783,6 +2848,94 @@ def _build_prediction_boundary_contract(route: str, strict: Dict[str, Any]) -> D
}
def _build_domain_invocation_contract(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
layers = _domain_invocation_layers()
return {
key: {
**value,
"route": route,
"used": True,
}
for key, value in layers.items()
}
def _build_output_template_contract(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"status": "used",
"route": route,
"language": "zh",
"required_sections": ["promise", "activation", "manifestation", "label", "confidence_boundary"],
"golden_test_status": "required",
"source_refs": [
"references/prediction-boundary-protocol.md",
"references/event_judgment_skeleton.md",
],
"instruction": "Final Chinese fortune output must map claims to promise/activation/manifestation/label and show confidence boundaries.",
}
def _build_mevg_collection_queue(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"status": "queued",
"trigger": "fortune_question_strict_workflow",
"route": route,
"required_jobs": [
"global_web_evidence",
"real_case_reference_search",
"source_grading",
"conflict_arbitration",
"unverified_claim_downgrade",
],
"cache_policy": "reuse_official_snapshot_and_external_evidence_cache_before_live_fetch",
"source_ref": "references/mandatory-verification-gate-protocol.md",
}
def _build_real_case_calibration_layer(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"status": "queued",
"route": route,
"domain_buckets": ["career", "finance", "relationship", "health", "rectification", "timing"],
"source_roots": ["references/real_case_studies", "docs/benchmark"],
"retrieval_policy": "domain_bucket_first_then_case_quality_gate",
"confidence_effect": "caps_confidence_until_matching_cases_are_attached",
}
def _build_technical_debt_contract(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"status": "tracked",
"route": route,
"narayana": {
"status": "partial",
"source_refs": ["references/bphs-ch48-narayana-dasha.md", "references/condition-dasha-complete.md"],
"open_items": [
"antardasha_pratyantar_oracle_parity",
"subperiod_boundary_regression",
"external_engine_crosscheck",
],
},
"tajika": {
"status": "partial",
"source_refs": ["references/tajika-yoga-complete-guide.md"],
"open_items": [
"solar_return_precision",
"muntha_placeholder_audit",
"annual_yoga_oracle_parity",
],
},
}
def _build_remaining_priority1_batch_queue() -> Dict[str, Any]:
return {
"status": "queued",
"next_batches": REMAINING_PRIORITY1_BATCH_QUEUE,
"boundary": "Do not promote remaining priority_1 materials without batch audit and tests.",
}
def _build_multi_reference_reading_summary(route: str, present: Dict[str, Any], strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"root_frame": _summary_root_frame(route, present),
@@ -2809,6 +2962,12 @@ def _attach_top_reader_contract(route: str, strict: Dict[str, Any]) -> Dict[str,
strict["technique_audit_summary"] = _build_technique_audit_summary(route, strict)
strict["adjudication_stages"] = _build_adjudication_stages(route, present, event_judgement)
strict["prediction_boundary_contract"] = _build_prediction_boundary_contract(route, strict)
strict["domain_invocation_contract"] = _build_domain_invocation_contract(route, strict)
strict["output_template_contract"] = _build_output_template_contract(route, strict)
strict["mevg_collection_queue"] = _build_mevg_collection_queue(route, strict)
strict["real_case_calibration_layer"] = _build_real_case_calibration_layer(route, strict)
strict["technical_debt_contract"] = _build_technical_debt_contract(route, strict)
strict["remaining_priority1_batch_queue"] = _build_remaining_priority1_batch_queue()
strict["multi_reference_reading_summary"] = _build_multi_reference_reading_summary(route, present, strict)
strict["official_day_signal_summary"] = _build_official_day_signal_summary(present.get("external_activation"))
strict["monthly_adjudication_summary"] = _build_monthly_adjudication_summary(route, strict)
+10
View File
@@ -777,3 +777,13 @@
- duplicate / obsolete / quarantine 8 个文件已列入 `blocked_non_runtime_layer`,并由 inventory gate 检查不进入 runtime source refs;重点包括 `kp-practical-event-timing.md` 与 `consultation-case-library.md`。
- Prompt Pack / API fallback / AI Chat 已同步 `prediction_boundary_contract`、核心 5、第二批 11、reference-only 3;AI Chat 上下文新增 `【Prediction Boundary Contract】` 段落。
- 真实 full-reading 回归确认:REDACTED_DATE REDACTED_TIME REDACTED_PLACE矿区样例中 relationship / career / finance 三条 strict contract 均带 prediction boundary,且 MEVG 与真实案例校准仍为 blocked。
## 2026-07-02T16:12:00+08:00 - 第二批领域调用层与后续队列合同
- 第二批 11 个 promote 源头已拆成四个领域调用层:`dasha_timing`、`varga_strength`、`annual_special`、`modifier_obstacle`;career / relationship / finance strict workflow 均暴露 `domain_invocation_contract`。
- 新增 `output_template_contract`,要求最终中文输出按 `promise / activation / manifestation / label / confidence_boundary` 组织;当前以合同和测试约束为主,后续可继续做 golden narrative snapshot。
- 新增 `mevg_collection_queue`,fortune strict workflow 会生成外部采集队列合同:global web evidence、real case search、source grading、conflict arbitration、unverified downgrade。
- 新增 `real_case_calibration_layer`,按 career / finance / relationship / health / rectification / timing 六个桶连接 `references/real_case_studies` 与 `docs/benchmark`。
- 新增 `technical_debt_contract`,诚实标记 Narayana 与 Tajika 仍为 `partial`:Narayana 需 Antardasha/Pratyantar oracle parity,Tajika 需 solar return precision、Muntha placeholder audit 与 annual yoga oracle parity。
- 前端 AI Prompt Pack 新增 `Source Governance` 面板,显示 core sources、reference-only、blocked non-runtime、confidence downgrade、MEVG queue、case calibration、Narayana/Tajika debt 与 next batches。
- 剩余 priority_1 队列明确为:`real_case_studies_batch1`、`rishi_ai_mcp_batch1`、`vedic_astro_skills_batch1`、`references_batch2`。
+63
View File
@@ -3987,6 +3987,69 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
'unverified_claim_policy': 'downgrade_or_block',
},
},
'domain_invocation_layers': {
'dasha_timing': {
'status': 'fallback_prompt_pack_only',
'source_refs': [
'references/vimshottari_dasha_guide.md',
'references/pratyantar-calculation-guide.md',
'references/condition-dasha-complete.md',
],
},
'varga_strength': {
'status': 'fallback_prompt_pack_only',
'source_refs': [
'references/divisional-chart-deep-reading.md',
'references/shadbala-complete-methodology.md',
'references/ashtakavarga-complete-system.md',
],
},
'annual_special': {
'status': 'fallback_prompt_pack_only',
'source_refs': [
'references/tajika-yoga-complete-guide.md',
'references/jaimini-complete-system.md',
'references/kp-astrology-complete-system.md',
],
},
'modifier_obstacle': {
'status': 'fallback_prompt_pack_only',
'source_refs': [
'references/argala-complete-guide.md',
'references/badhaka-obstacle-planet-guide.md',
],
},
},
'output_template_contract': {
'status': 'fallback_prompt_pack_only',
'language': 'zh',
'required_sections': ['promise', 'activation', 'manifestation', 'label', 'confidence_boundary'],
'golden_test_status': 'required',
},
'mevg_collection_queue': {
'status': 'queued',
'trigger': 'fortune_question_strict_workflow',
'required_jobs': ['global_web_evidence', 'source_grading', 'conflict_arbitration'],
},
'real_case_calibration_layer': {
'status': 'queued',
'domain_buckets': ['career', 'finance', 'relationship', 'health', 'rectification', 'timing'],
'source_roots': ['references/real_case_studies', 'docs/benchmark'],
},
'technical_debt_contract': {
'status': 'tracked',
'narayana': {'status': 'partial', 'open_items': ['antardasha_pratyantar_oracle_parity']},
'tajika': {'status': 'partial', 'open_items': ['solar_return_precision', 'muntha_placeholder_audit']},
},
'remaining_priority1_batch_queue': {
'status': 'queued',
'next_batches': [
'real_case_studies_batch1',
'rishi_ai_mcp_batch1',
'vedic_astro_skills_batch1',
'references_batch2',
],
},
'vedastro_official_full_snapshot': vedastro_official_full_snapshot,
'vedastro_overview': vedastro_overview,
'guided_topics': guided_topics,
+42
View File
@@ -1433,6 +1433,12 @@ def _compact_strict_workflow_contract(strict):
'technique_audit_summary': strict.get('technique_audit_summary') or {},
'adjudication_stages': strict.get('adjudication_stages') or {},
'prediction_boundary_contract': strict.get('prediction_boundary_contract') or {},
'domain_invocation_contract': strict.get('domain_invocation_contract') or {},
'output_template_contract': strict.get('output_template_contract') or {},
'mevg_collection_queue': strict.get('mevg_collection_queue') or {},
'real_case_calibration_layer': strict.get('real_case_calibration_layer') or {},
'technical_debt_contract': strict.get('technical_debt_contract') or {},
'remaining_priority1_batch_queue': strict.get('remaining_priority1_batch_queue') or {},
'multi_reference_reading_summary': strict.get('multi_reference_reading_summary') or {},
'monthly_adjudication_summary': strict.get('monthly_adjudication_summary') or {},
'official_day_signal_summary': strict.get('official_day_signal_summary') or {},
@@ -1564,6 +1570,36 @@ def _build_ai_prompt_pack(report):
if isinstance(primary_strict_contract, dict)
else {}
)
primary_domain_invocation_layers = (
primary_strict_contract.get('domain_invocation_contract')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_output_template_contract = (
primary_strict_contract.get('output_template_contract')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_mevg_collection_queue = (
primary_strict_contract.get('mevg_collection_queue')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_real_case_calibration_layer = (
primary_strict_contract.get('real_case_calibration_layer')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_technical_debt_contract = (
primary_strict_contract.get('technical_debt_contract')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_remaining_priority1_batch_queue = (
primary_strict_contract.get('remaining_priority1_batch_queue')
if isinstance(primary_strict_contract, dict)
else {}
)
primary_audit = (
primary_strict_contract.get('technique_audit_summary')
if isinstance(primary_strict_contract, dict)
@@ -1655,6 +1691,12 @@ def _build_ai_prompt_pack(report):
'missing_refs': interpretation_source_audit.get('missing_refs') or [],
},
'prediction_boundary_contract': primary_prediction_boundary_contract or {},
'domain_invocation_layers': primary_domain_invocation_layers or {},
'output_template_contract': primary_output_template_contract or {},
'mevg_collection_queue': primary_mevg_collection_queue or {},
'real_case_calibration_layer': primary_real_case_calibration_layer or {},
'technical_debt_contract': primary_technical_debt_contract or {},
'remaining_priority1_batch_queue': primary_remaining_priority1_batch_queue or {},
'strict_workflow_primary_route': strict_workflow_primary_route,
'strict_workflow_routes_available': list(strict_workflow_contracts.keys()),
'strict_workflow_contracts': strict_workflow_contracts,
+16
View File
@@ -304,6 +304,22 @@ def test_full_reading_reports_ayanamsa_metadata_and_ai_prompt_pack() -> None:
"manifestation",
"label",
]
domain_layers = prompt_pack["evidence_snapshot"]["domain_invocation_layers"]
assert domain_layers["dasha_timing"]["source_refs"] == [
"references/vimshottari_dasha_guide.md",
"references/pratyantar-calculation-guide.md",
"references/condition-dasha-complete.md",
]
assert domain_layers["varga_strength"]["source_refs"] == [
"references/divisional-chart-deep-reading.md",
"references/shadbala-complete-methodology.md",
"references/ashtakavarga-complete-system.md",
]
assert prompt_pack["evidence_snapshot"]["output_template_contract"]["required_sections"][-1] == "confidence_boundary"
assert prompt_pack["evidence_snapshot"]["mevg_collection_queue"]["status"] == "queued"
assert prompt_pack["evidence_snapshot"]["real_case_calibration_layer"]["status"] == "queued"
assert prompt_pack["evidence_snapshot"]["technical_debt_contract"]["tajika"]["status"] == "partial"
assert prompt_pack["evidence_snapshot"]["remaining_priority1_batch_queue"]["next_batches"][0] == "real_case_studies_batch1"
functional_rows = [row for row in audit_table if row["technique"] == "Functional Benefic/Malefic"]
assert functional_rows
assert functional_rows[0]["status"] == "used"
+5
View File
@@ -337,6 +337,11 @@ def test_frontend_consumes_top_reader_contract_in_prompt_pack_and_ai_chat() -> N
assert "unverified_claim_policy" in ai_chat
assert "technique_audit_summary" in ai_chat
assert "【Top Reader Contract】" in ai_chat
assert "renderInterpretationSourceGovernancePanel" in main
assert "Source Governance" in main
assert "reference-only" in main
assert "blocked non-runtime" in main
assert "confidence downgrade" in main
assert ".ai-prompt-pack-contract-card" in style
@@ -0,0 +1,191 @@
#!/usr/bin/env python3
"""Contracts for the next interpretation-source pipeline stage."""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
from mcp_server import _collect_strict_evidence, _existing_interpretation_source_pack
ROOT = Path(__file__).resolve().parents[1]
DASHA_TIMING = [
"references/vimshottari_dasha_guide.md",
"references/pratyantar-calculation-guide.md",
"references/condition-dasha-complete.md",
]
VARGA_STRENGTH = [
"references/divisional-chart-deep-reading.md",
"references/shadbala-complete-methodology.md",
"references/ashtakavarga-complete-system.md",
]
ANNUAL_SPECIAL = [
"references/tajika-yoga-complete-guide.md",
"references/jaimini-complete-system.md",
"references/kp-astrology-complete-system.md",
]
MODIFIER_OBSTACLE = [
"references/argala-complete-guide.md",
"references/badhaka-obstacle-planet-guide.md",
]
def _base_modules() -> dict:
return {
"modules": {
"chart": {
"ascendant": {"sign": "Leo"},
"planets": {
"Sun": {"status": "中性(Neutral)"},
"Moon": {"status": "中性(Neutral)"},
"Mercury": {"status": "中性(Neutral)"},
"Venus": {"status": "中性(Neutral)"},
"Jupiter": {"status": "中性(Neutral)"},
"Saturn": {"status": "中性(Neutral)"},
},
},
"varga_full": {
"D10_Dasamsa": {"summary": "career varga present"},
"D9_Navamsha": {"summary": "relationship varga present"},
"D2_Hora": {"summary": "wealth varga present"},
"D11_Rudramsha": {"summary": "gains varga present"},
},
"special_lagnas": {
"A10_Karma_Pada": {"sign": "Capricorn", "lord": "Saturn"},
"Upapada_Lagna": {"sign": "Libra", "lord": "Venus"},
},
"jaimini": {
"karakas": {
"Amatyakaraka": {"planet": "Mercury"},
"Atmakaraka": {"planet": "Sun"},
"Darakaraka": {"planet": "Venus"},
},
"karakamsha": {"karakamsha_sign": "Leo", "karakamsha_lord": "Sun"},
},
"dasha": {"current_dasha": {"mahadasha": "Mercury", "antardasha": "Sun"}},
"narayana_dasha": {
"current_dasha": {
"md": {"sign": "Capricorn", "lord": "Saturn"},
"ad": {"sign": "Aquarius", "lord": "Saturn"},
"pd": {"sign": "Pisces", "lord": "Jupiter"},
}
},
"dasa_convergence": {
"domain_activations": {
"career_status": {"convergence_level": "L2", "probability": "35-50%"},
"marriage_relationship": {"convergence_level": "L2", "probability": "35-50%"},
"wealth_income": {"convergence_level": "L2", "probability": "35-50%"},
}
},
}
}
def _run_engine(*args: str) -> dict:
completed = subprocess.run(
[sys.executable, "scripts/jyotish_engine.py", *args],
cwd=ROOT,
check=False,
text=True,
capture_output=True,
timeout=180,
)
assert completed.returncode == 0, completed.stderr[-2000:] or completed.stdout[-2000:]
return json.loads(completed.stdout)
def test_promote_batch2_is_split_into_domain_invocation_layers() -> None:
source_pack = _existing_interpretation_source_pack()
domain_layers = source_pack["domain_invocation_layers"]
assert domain_layers["dasha_timing"]["source_refs"] == DASHA_TIMING
assert domain_layers["varga_strength"]["source_refs"] == VARGA_STRENGTH
assert domain_layers["annual_special"]["source_refs"] == ANNUAL_SPECIAL
assert domain_layers["modifier_obstacle"]["source_refs"] == MODIFIER_OBSTACLE
assert domain_layers["dasha_timing"]["required_in_routes"] == ["career", "relationship", "finance"]
assert domain_layers["varga_strength"]["required_in_routes"] == ["career", "relationship", "finance"]
for route in ["career", "relationship", "finance"]:
strict = _collect_strict_evidence(route, _base_modules())
invocation = strict["domain_invocation_contract"]
assert invocation["dasha_timing"]["source_refs"] == DASHA_TIMING
assert invocation["varga_strength"]["source_refs"] == VARGA_STRENGTH
assert invocation["annual_special"]["source_refs"] == ANNUAL_SPECIAL
assert invocation["modifier_obstacle"]["source_refs"] == MODIFIER_OBSTACLE
def test_output_template_mevg_case_and_technical_debt_contracts_are_present() -> None:
strict = _collect_strict_evidence("career", _base_modules())
template = strict["output_template_contract"]
assert template["required_sections"] == ["promise", "activation", "manifestation", "label", "confidence_boundary"]
assert template["language"] == "zh"
assert template["golden_test_status"] == "required"
mevg_queue = strict["mevg_collection_queue"]
assert mevg_queue["status"] == "queued"
assert mevg_queue["trigger"] == "fortune_question_strict_workflow"
assert "global_web_evidence" in mevg_queue["required_jobs"]
assert "source_grading" in mevg_queue["required_jobs"]
assert "conflict_arbitration" in mevg_queue["required_jobs"]
case_layer = strict["real_case_calibration_layer"]
assert case_layer["status"] == "queued"
assert case_layer["domain_buckets"] == ["career", "finance", "relationship", "health", "rectification", "timing"]
assert case_layer["source_roots"] == ["references/real_case_studies", "docs/benchmark"]
debt = strict["technical_debt_contract"]
assert debt["narayana"]["status"] == "partial"
assert "antardasha_pratyantar_oracle_parity" in debt["narayana"]["open_items"]
assert debt["tajika"]["status"] == "partial"
assert "solar_return_precision" in debt["tajika"]["open_items"]
assert "muntha_placeholder_audit" in debt["tajika"]["open_items"]
def test_prompt_pack_frontend_and_remaining_batch_queue_expose_next_stage_contracts() -> None:
result = _run_engine(
"full-reading",
"--year",
"REDACTED_YEAR",
"--month",
"4",
"--day",
"17",
"--hour",
"14",
"--minute",
"49",
"--lat",
"36.466667",
"--lon",
"114.2",
"--tz",
"8",
"--today",
"2026-07-02",
"--transit-date",
"2026-07-02",
)
snapshot = result["ai_prompt_pack"]["evidence_snapshot"]
assert snapshot["domain_invocation_layers"]["dasha_timing"]["source_refs"] == DASHA_TIMING
assert snapshot["output_template_contract"]["required_sections"][-1] == "confidence_boundary"
assert snapshot["mevg_collection_queue"]["status"] == "queued"
assert snapshot["real_case_calibration_layer"]["status"] == "queued"
assert snapshot["technical_debt_contract"]["tajika"]["status"] == "partial"
assert snapshot["remaining_priority1_batch_queue"]["next_batches"] == [
"real_case_studies_batch1",
"rishi_ai_mcp_batch1",
"vedic_astro_skills_batch1",
"references_batch2",
]
main_js = (ROOT / "jyotish-app" / "main.js").read_text(encoding="utf-8")
assert "renderInterpretationSourceGovernancePanel" in main_js
assert "Source Governance" in main_js
assert "reference-only" in main_js
assert "blocked non-runtime" in main_js