Add Rangacharya comparison variant
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"created": "2026-07-16",
|
||||
"cards": [
|
||||
{
|
||||
"id": "rangacharya_core_arudha",
|
||||
"title": "Rangacharya Arudha core counting",
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false,
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Pending formula-level transcription and source verification.",
|
||||
"notes": "Covers AL, A7, A10, UL, and A1-A12 variant counting."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "active_effective_lagna",
|
||||
"title": "Active Lagna and Effective Lagna",
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false,
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Pending formula-level transcription and source verification.",
|
||||
"notes": "Runtime may report placeholder metadata only."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "prakriti_sanmukha",
|
||||
"title": "Prakriti Chakra and Sanmukha",
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "formula direction and exception rules need source-card verification",
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Blocked until rule text is verified.",
|
||||
"notes": "No runtime calculation allowed yet."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_special_mappings",
|
||||
"title": "Special divisional and graha mappings",
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "Krishnamisra/Somanatha Navamsa, Parivritti/Somanatha Drekkana, Pancansa, and Graha Chakra need separate fixtures",
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Blocked until mapping tables are verified.",
|
||||
"notes": "Raw descriptive output only after fixtures exist."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_named_yogas",
|
||||
"title": "Named Rangacharya yogas",
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "named yogas need formula-level source cards and golden fixtures",
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Blocked until each yoga has its own source card.",
|
||||
"notes": "Includes Dhana, Nirdhana, Kemadruma, Vahana, Bandhana, Dustamarana, Saukhya, Buddhi, Raja, Manipravala, Amatya, Senadhipatya, Chandradhi/Lagnadhi, Karakamsa, Yogada, Kevala, and Aspecting Graha."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_ul_family_rules",
|
||||
"title": "UL relationship and family rules",
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "relationship, child, miscarriage, and adoption claims need real-case calibration",
|
||||
"evidence": {
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"formula_text": "Blocked until source and case validation.",
|
||||
"notes": "May only become rule-hit reporting before calibration."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "article_warehouse_future_tracks",
|
||||
"title": "Future article-warehouse tracks",
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "registered only; out of Rangacharya runtime scope",
|
||||
"evidence": {
|
||||
"source_ids": ["local_article_warehouse_20260716"],
|
||||
"formula_text": "Blocked until hash, license, and technique-level extraction.",
|
||||
"notes": "Includes Tithi Lord, Panchapakshi, Rashi Tulya Navamsa, Bhrigu Pada Dasha, Tajika, Darakaraka, and spouse rules."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"created": "2026-07-16",
|
||||
"sources": [
|
||||
{
|
||||
"id": "uploaded_screenshots_20260716",
|
||||
"kind": "user_uploaded_screenshots",
|
||||
"paths": [
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3502.PNG",
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3503.PNG",
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3504.PNG",
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3505.PNG",
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3506.PNG",
|
||||
"/Users/wuyongnaren/文件仓库/印度占星文章/260716/IMG_3507.PNG"
|
||||
],
|
||||
"sha256": {
|
||||
"IMG_3502.PNG": "1cf628c3f5dfac372c719eba442f0cc8325114fabea4cc01855e00247bc9c031",
|
||||
"IMG_3503.PNG": "f3c1f3c67a3add9c6c586e60e18186e86549ca4a5b5a1e6a6c07cc4fe223e1de",
|
||||
"IMG_3504.PNG": "6c884011b1fe458da2f23a4e3e5acaf48c6ea72cd402b51b2942b3a28cee5de1",
|
||||
"IMG_3505.PNG": "16af004f553fde2c1ab5d0bc7968deba46040a02e7726f735f174523e6bdccee",
|
||||
"IMG_3506.PNG": "a8732bc73767983130683152079eb44697d4686435452e71d2e742f8b16ed888",
|
||||
"IMG_3507.PNG": "1ff2bbc69a3fe1dab17d742437000d9938d9b5bb5e818831dafc9bf0e05a152e"
|
||||
},
|
||||
"license": "user_private_reference",
|
||||
"privacy": "private",
|
||||
"runtime_use": "reference_only_until_formula_verified",
|
||||
"extraction_status": "located_and_hashed_ocr_blocked_tesseract_missing"
|
||||
},
|
||||
{
|
||||
"id": "local_article_warehouse_20260716",
|
||||
"kind": "local_research_archive",
|
||||
"path": "/Users/wuyongnaren/文件仓库/印度占星文章",
|
||||
"license": "unknown",
|
||||
"privacy": "local_research",
|
||||
"runtime_use": "manifest_only_until_hash_and_license_review"
|
||||
},
|
||||
{
|
||||
"id": "kimi_agent_archive_20260716",
|
||||
"kind": "local_training_archive",
|
||||
"path": "/Users/wuyongnaren/Downloads/_整理候选/安装包与压缩包/Kimi_Agent_高维印度占星师.zip",
|
||||
"license": "unknown",
|
||||
"privacy": "local_research",
|
||||
"runtime_use": "reference_only_until_license_review"
|
||||
},
|
||||
{
|
||||
"id": "vedastro_official",
|
||||
"kind": "external_oracle",
|
||||
"url": "https://github.com/VedAstro/VedAstro",
|
||||
"license": "MIT",
|
||||
"privacy": "public",
|
||||
"runtime_use": "oracle_raw_reference"
|
||||
},
|
||||
{
|
||||
"id": "pyjhora_official",
|
||||
"kind": "external_oracle",
|
||||
"url": "https://github.com/naturalstupid/PyJHora",
|
||||
"license": "AGPL-3.0",
|
||||
"privacy": "public",
|
||||
"runtime_use": "isolated_external_process_only"
|
||||
},
|
||||
{
|
||||
"id": "jyotishganit_official",
|
||||
"kind": "external_oracle",
|
||||
"url": "https://github.com/northtara/jyotishganit",
|
||||
"license": "MIT",
|
||||
"privacy": "public",
|
||||
"runtime_use": "oracle_raw_reference"
|
||||
},
|
||||
{
|
||||
"id": "dashaflow_official",
|
||||
"kind": "external_formula_reference",
|
||||
"url": "https://github.com/adarshj322/dashaflow",
|
||||
"license": "MIT",
|
||||
"privacy": "public",
|
||||
"runtime_use": "formula_reference_only"
|
||||
}
|
||||
],
|
||||
"validation_ladder": [
|
||||
"transcribed",
|
||||
"source_verified",
|
||||
"golden_verified",
|
||||
"engine_cross_checked",
|
||||
"case_calibrated",
|
||||
"adjudication_enabled",
|
||||
"blocked"
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"id": "rangacharya_core_arudha",
|
||||
"label": "Rangacharya Arudha core counting",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false
|
||||
},
|
||||
{
|
||||
"id": "active_effective_lagna",
|
||||
"label": "Active Lagna and Effective Lagna",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_special_mappings",
|
||||
"label": "Krishnamisra/Somanatha Navamsa, Parivritti/Somanatha Drekkana, Pancansa Graha, Graha Chakra",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "needs formula-level source cards before runtime use"
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_named_yogas",
|
||||
"label": "Dhana/Nirdhana/Kemadruma and other named yogas",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "needs formula-level source cards before runtime use"
|
||||
},
|
||||
{
|
||||
"id": "article_warehouse_future_tracks",
|
||||
"label": "Tithi Lord, Panchapakshi, Rashi Tulya Navamsa, Bhrigu Pada Dasha, Tajika, Darakaraka, spouse rules",
|
||||
"source_ids": ["local_article_warehouse_20260716"],
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "registered for later source governance; out of Rangacharya Phase 1 runtime scope"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1695,6 +1695,40 @@
|
||||
},
|
||||
"conclusion_policy": "Supporting evidence only; it can raise/lower confidence but cannot by itself decide an event or timing claim."
|
||||
},
|
||||
"rangacharya_jaimini_variant": {
|
||||
"audit_label": "Rangacharya Jaimini Variant",
|
||||
"commands": [
|
||||
"jaimini"
|
||||
],
|
||||
"conclusion_policy": "Display current-vs-variant differences only; do not use for verdicts or timing.",
|
||||
"domains": [
|
||||
"jaimini",
|
||||
"arudha",
|
||||
"experimental",
|
||||
"audit"
|
||||
],
|
||||
"entry_type": "experimental_variant",
|
||||
"evidence_role": "comparison_only",
|
||||
"knowledge_refs": [
|
||||
"references/rangacharya_source_manifest.json",
|
||||
"references/rangacharya_source_cards.json",
|
||||
"docs/superpowers/specs/2026-07-16-rangacharya-vedastro-design.md"
|
||||
],
|
||||
"missing_impact": "Rangacharya-specific Arudha and named-yoga rules remain unavailable for adjudication until source cards, golden fixtures, oracle comparison, and case calibration pass.",
|
||||
"name": "Rangacharya / Iranganti Jaimini Variant",
|
||||
"note": "Rangacharya variant has a tested current-vs-variant diff path and API variant output, but adjudication remains disabled until source-card validation and golden fixtures pass.",
|
||||
"output_paths": [
|
||||
"result.rangacharya",
|
||||
"result.rangacharya_diff"
|
||||
],
|
||||
"status": "comparison-only",
|
||||
"user_visibility": "expert_audit",
|
||||
"verification_level": {
|
||||
"calculation": "experimental",
|
||||
"prediction": "blocked",
|
||||
"rule": "blocked"
|
||||
}
|
||||
},
|
||||
"rashi_tulya_navamsa": {
|
||||
"name": "Rashi Tulya Navamsa / 本命对分盘分析",
|
||||
"domains": [
|
||||
|
||||
@@ -5947,6 +5947,13 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
allowed_modes = {'all', 'karaka', 'dasha', 'karakamsha', 'arudha', 'special'}
|
||||
if mode not in allowed_modes:
|
||||
raise BadRequest(f'mode must be one of: {", ".join(sorted(allowed_modes))}')
|
||||
variant = body.get('variant', 'current')
|
||||
if not isinstance(variant, str):
|
||||
raise BadRequest('variant must be a string')
|
||||
variant = variant.strip().lower() or 'current'
|
||||
allowed_variants = {'current', 'rangacharya', 'all'}
|
||||
if variant not in allowed_variants:
|
||||
raise BadRequest(f'variant must be one of: {", ".join(sorted(allowed_variants))}')
|
||||
antardasha = bool(body.get('antardasha', False))
|
||||
year = self._get_int(body, 'year', datetime.now().year, 1800, 2400)
|
||||
month = self._get_int(body, 'month', 1, 1, 12)
|
||||
@@ -5976,6 +5983,12 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
if mode in ('all', 'arudha'):
|
||||
result['arudha_padas'] = jaimini.calc_arudha_padas(asc_sign_idx, planet_lons)
|
||||
result['graha_padas'] = jaimini.calc_graha_padas(planet_lons)
|
||||
if variant in ('rangacharya', 'all'):
|
||||
rangacharya = _load_local_module('rangacharya')
|
||||
rangacharya_result = rangacharya.calc_rangacharya_variant(asc_sign_idx, planet_lons)
|
||||
result['rangacharya'] = rangacharya_result
|
||||
current_arudha = result.get('arudha_padas') or jaimini.calc_arudha_padas(asc_sign_idx, planet_lons)
|
||||
result['rangacharya_diff'] = rangacharya.diff_current_vs_rangacharya(current_arudha, rangacharya_result)
|
||||
if mode in ('all', 'special'):
|
||||
result['special_lagnas'] = jaimini.calc_special_lagnas(asc_sign_idx, hour, minute + second / 60.0)
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
"""Experimental Rangacharya/Jaimini variant.
|
||||
|
||||
All outputs are blocked from adjudication until formula-level validation passes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Mapping
|
||||
|
||||
|
||||
SIGNS = [
|
||||
"Aries",
|
||||
"Taurus",
|
||||
"Gemini",
|
||||
"Cancer",
|
||||
"Leo",
|
||||
"Virgo",
|
||||
"Libra",
|
||||
"Scorpio",
|
||||
"Sagittarius",
|
||||
"Capricorn",
|
||||
"Aquarius",
|
||||
"Pisces",
|
||||
]
|
||||
|
||||
SOURCE_CARDS_PATH = Path(__file__).resolve().parent.parent / "references" / "rangacharya_source_cards.json"
|
||||
|
||||
|
||||
class RangacharyaValidationError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def _source_cards() -> Dict[str, Dict[str, Any]]:
|
||||
try:
|
||||
data = json.loads(SOURCE_CARDS_PATH.read_text(encoding="utf-8"))
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {}
|
||||
return {str(card.get("id")): dict(card) for card in data.get("cards", []) if card.get("id")}
|
||||
|
||||
|
||||
def _card_meta(card_id: str) -> Dict[str, Any]:
|
||||
card = _source_cards().get(card_id, {})
|
||||
status = str(card.get("status") or "blocked")
|
||||
meta = {
|
||||
"source_card_id": card_id,
|
||||
"source_card_status": status,
|
||||
"validation_status": status,
|
||||
"adjudication_enabled": False,
|
||||
}
|
||||
if status != "source_verified":
|
||||
meta["blocked_reason"] = card.get("blocked_reason") or "source card is not verified for adjudication"
|
||||
return meta
|
||||
|
||||
|
||||
def _sign_name(index: int) -> str:
|
||||
return SIGNS[index % 12]
|
||||
|
||||
|
||||
def _placeholder_pada(label: str, asc_sign_idx: int, source_house: int) -> Dict[str, Any]:
|
||||
sign_idx = (asc_sign_idx + source_house - 1) % 12
|
||||
return {
|
||||
"label": label,
|
||||
"sign": _sign_name(sign_idx),
|
||||
"sign_index": sign_idx,
|
||||
"source_house": source_house,
|
||||
"note": "Rangacharya formula pending source-card implementation",
|
||||
**_card_meta("rangacharya_core_arudha"),
|
||||
}
|
||||
|
||||
|
||||
def calc_rangacharya_variant(asc_sign_idx: int, planet_longitudes: Mapping[str, float]) -> Dict[str, Any]:
|
||||
asc_sign_idx %= 12
|
||||
arudha_padas = {
|
||||
"AL": _placeholder_pada("AL", asc_sign_idx, 1),
|
||||
"A7": _placeholder_pada("A7", asc_sign_idx, 7),
|
||||
"A10": _placeholder_pada("A10", asc_sign_idx, 10),
|
||||
"UL": _placeholder_pada("UL", asc_sign_idx, 12),
|
||||
}
|
||||
return {
|
||||
"variant": "rangacharya",
|
||||
"status": "experimental_not_for_adjudication",
|
||||
"adjudication_enabled": False,
|
||||
"source_status": "transcribed",
|
||||
"active_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
**_card_meta("active_effective_lagna"),
|
||||
},
|
||||
"effective_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
**_card_meta("active_effective_lagna"),
|
||||
},
|
||||
"arudha_padas": arudha_padas,
|
||||
"input_planets_present": sorted(planet_longitudes),
|
||||
}
|
||||
|
||||
|
||||
def _flatten(prefix: str, value: Any) -> Dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
return {prefix: value}
|
||||
rows: Dict[str, Any] = {}
|
||||
for key, child in value.items():
|
||||
child_key = f"{prefix}.{key}" if prefix else str(key)
|
||||
rows.update(_flatten(child_key, child))
|
||||
return rows
|
||||
|
||||
|
||||
def diff_current_vs_rangacharya(current: Mapping[str, Any], variant: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
current_flat = _flatten("", dict(current))
|
||||
variant_flat = _flatten("", dict(variant.get("arudha_padas", variant)))
|
||||
differences = []
|
||||
for key in sorted(set(current_flat) | set(variant_flat)):
|
||||
current_value = current_flat.get(key)
|
||||
variant_value = variant_flat.get(key)
|
||||
if current_value != variant_value:
|
||||
differences.append({"key": key, "current": current_value, "rangacharya": variant_value})
|
||||
return {
|
||||
"current_algorithm": "current_jaimini",
|
||||
"variant_algorithm": "rangacharya",
|
||||
"adjudication_enabled": False,
|
||||
"differences": differences,
|
||||
}
|
||||
|
||||
|
||||
def validation_summary(result: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
statuses = []
|
||||
for key, value in _flatten("", dict(result)).items():
|
||||
if key.endswith("validation_status"):
|
||||
statuses.append(str(value))
|
||||
blocking = sorted({status for status in statuses if status != "adjudication_enabled"})
|
||||
return {
|
||||
"adjudication_enabled": bool(result.get("adjudication_enabled")) and not blocking,
|
||||
"blocking_statuses": blocking,
|
||||
}
|
||||
|
||||
|
||||
def assert_adjudication_allowed(result: Mapping[str, Any]) -> None:
|
||||
summary = validation_summary(result)
|
||||
if not summary["adjudication_enabled"]:
|
||||
raise RangacharyaValidationError(
|
||||
"Rangacharya variant is not adjudication-enabled; validation gates are incomplete"
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Readiness report for the experimental Rangacharya variant."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CARDS_PATH = ROOT / "references" / "rangacharya_source_cards.json"
|
||||
MANIFEST_PATH = ROOT / "references" / "rangacharya_source_manifest.json"
|
||||
|
||||
|
||||
def _load_json(path: Path) -> Dict[str, Any]:
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {}
|
||||
|
||||
|
||||
def build_report() -> Dict[str, Any]:
|
||||
cards_payload = _load_json(CARDS_PATH)
|
||||
manifest_payload = _load_json(MANIFEST_PATH)
|
||||
cards = {}
|
||||
blocked = []
|
||||
transcribed = []
|
||||
for card in cards_payload.get("cards", []):
|
||||
card_id = str(card.get("id") or "")
|
||||
if not card_id:
|
||||
continue
|
||||
status = str(card.get("status") or "blocked")
|
||||
adjudication_enabled = bool(card.get("adjudication_enabled"))
|
||||
cards[card_id] = {
|
||||
"status": status,
|
||||
"adjudication_enabled": adjudication_enabled,
|
||||
"blocked_reason": card.get("blocked_reason") or "",
|
||||
}
|
||||
if status == "blocked" or not adjudication_enabled:
|
||||
blocked.append(card_id)
|
||||
if status == "transcribed":
|
||||
transcribed.append(card_id)
|
||||
return {
|
||||
"scope": "rangacharya_readiness",
|
||||
"manifest_available": bool(manifest_payload),
|
||||
"source_cards_available": bool(cards_payload),
|
||||
"adjudication_enabled": bool(cards) and not blocked,
|
||||
"card_count": len(cards),
|
||||
"blocked_count": len(blocked),
|
||||
"transcribed_count": len(transcribed),
|
||||
"blocked_cards": blocked,
|
||||
"transcribed_cards": transcribed,
|
||||
"cards": cards,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(json.dumps(build_report(), ensure_ascii=False, indent=2, sort_keys=True))
|
||||
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts")
|
||||
if SCRIPTS not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS)
|
||||
|
||||
from jyotish_api_server import JyotishAPIHandler # noqa: E402
|
||||
|
||||
|
||||
def _handler() -> JyotishAPIHandler:
|
||||
return JyotishAPIHandler.__new__(JyotishAPIHandler)
|
||||
|
||||
|
||||
def _planets():
|
||||
return {
|
||||
"Sun": {"lon": 10.0},
|
||||
"Moon": {"lon": 45.0},
|
||||
"Mars": {"lon": 80.0},
|
||||
"Mercury": {"lon": 110.0},
|
||||
"Jupiter": {"lon": 145.0},
|
||||
"Venus": {"lon": 200.0},
|
||||
"Saturn": {"lon": 250.0},
|
||||
"Rahu": {"lon": 300.0},
|
||||
"Ketu": {"lon": 120.0},
|
||||
}
|
||||
|
||||
|
||||
def test_jaimini_default_does_not_include_rangacharya():
|
||||
result = _handler()._compute_jaimini({
|
||||
"mode": "arudha",
|
||||
"ascendant": {"lon": 0.0},
|
||||
"planets": _planets(),
|
||||
})
|
||||
assert "rangacharya" not in result["result"]
|
||||
assert "rangacharya_diff" not in result["result"]
|
||||
|
||||
|
||||
def test_jaimini_variant_all_includes_current_variant_and_diff():
|
||||
result = _handler()._compute_jaimini({
|
||||
"mode": "arudha",
|
||||
"variant": "all",
|
||||
"ascendant": {"lon": 0.0},
|
||||
"planets": _planets(),
|
||||
})
|
||||
assert result["result"]["rangacharya"]["adjudication_enabled"] is False
|
||||
assert result["result"]["rangacharya_diff"]["adjudication_enabled"] is False
|
||||
assert result["result"]["rangacharya"]["arudha_padas"]["AL"]["source_card_status"] == "transcribed"
|
||||
assert result["result"]["rangacharya"]["active_lagna"]["source_card_id"] == "active_effective_lagna"
|
||||
@@ -0,0 +1,16 @@
|
||||
import pytest
|
||||
|
||||
from scripts import rangacharya
|
||||
|
||||
|
||||
def test_assert_adjudication_allowed_rejects_default_variant():
|
||||
result = rangacharya.calc_rangacharya_variant(0, {"Sun": 10.0})
|
||||
with pytest.raises(rangacharya.RangacharyaValidationError):
|
||||
rangacharya.assert_adjudication_allowed(result)
|
||||
|
||||
|
||||
def test_validation_summary_lists_blocking_rules():
|
||||
result = rangacharya.calc_rangacharya_variant(0, {"Sun": 10.0})
|
||||
summary = rangacharya.validation_summary(result)
|
||||
assert summary["adjudication_enabled"] is False
|
||||
assert summary["blocking_statuses"]
|
||||
@@ -0,0 +1,14 @@
|
||||
from scripts import rangacharya_readiness
|
||||
|
||||
|
||||
def test_readiness_reports_blocked_until_no_adjudication_cards():
|
||||
report = rangacharya_readiness.build_report()
|
||||
assert report["scope"] == "rangacharya_readiness"
|
||||
assert report["adjudication_enabled"] is False
|
||||
assert report["blocked_count"] >= 1
|
||||
assert "rangacharya_core_arudha" in report["cards"]
|
||||
|
||||
|
||||
def test_readiness_does_not_expose_secrets():
|
||||
report = rangacharya_readiness.build_report()
|
||||
assert "sk_live_" not in str(report)
|
||||
@@ -0,0 +1,17 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REGISTRY = ROOT / "references" / "technique_registry.json"
|
||||
|
||||
|
||||
def test_rangacharya_variant_registered_as_comparison_only():
|
||||
techniques = json.loads(REGISTRY.read_text(encoding="utf-8"))["techniques"]
|
||||
entry = techniques["rangacharya_jaimini_variant"]
|
||||
assert entry["status"] == "comparison-only"
|
||||
assert entry["verification_level"]["calculation"] == "experimental"
|
||||
assert entry["verification_level"]["prediction"] == "blocked"
|
||||
assert entry["conclusion_policy"] == "Display current-vs-variant differences only; do not use for verdicts or timing."
|
||||
assert entry["evidence_role"] == "comparison_only"
|
||||
assert "references/rangacharya_source_cards.json" in entry["knowledge_refs"]
|
||||
@@ -0,0 +1,50 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CARDS = Path("references/rangacharya_source_cards.json")
|
||||
|
||||
|
||||
REQUIRED_CARD_IDS = {
|
||||
"rangacharya_core_arudha",
|
||||
"active_effective_lagna",
|
||||
"prakriti_sanmukha",
|
||||
"rangacharya_special_mappings",
|
||||
"rangacharya_named_yogas",
|
||||
"rangacharya_ul_family_rules",
|
||||
"article_warehouse_future_tracks",
|
||||
}
|
||||
|
||||
|
||||
def _cards():
|
||||
return json.loads(CARDS.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def test_source_cards_exist_with_required_schema():
|
||||
data = _cards()
|
||||
assert data["schema_version"] == 1
|
||||
assert isinstance(data["cards"], list)
|
||||
for card in data["cards"]:
|
||||
assert card["id"]
|
||||
assert card["status"] in {
|
||||
"transcribed",
|
||||
"source_verified",
|
||||
"golden_verified",
|
||||
"engine_cross_checked",
|
||||
"case_calibrated",
|
||||
"blocked",
|
||||
}
|
||||
assert card["adjudication_enabled"] is False
|
||||
assert "evidence" in card
|
||||
|
||||
|
||||
def test_source_cards_cover_phase2_rule_groups():
|
||||
data = _cards()
|
||||
found = {card["id"] for card in data["cards"]}
|
||||
assert REQUIRED_CARD_IDS <= found
|
||||
|
||||
|
||||
def test_source_cards_do_not_contain_secrets():
|
||||
text = CARDS.read_text(encoding="utf-8")
|
||||
assert "sk_live_" not in text
|
||||
assert "VEDASTRO_API_KEY" not in text
|
||||
@@ -0,0 +1,50 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MANIFEST = Path("references/rangacharya_source_manifest.json")
|
||||
|
||||
|
||||
def test_manifest_exists_and_has_required_sections():
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
assert data["schema_version"] == 1
|
||||
assert "sources" in data
|
||||
assert "rules" in data
|
||||
assert "validation_ladder" in data
|
||||
|
||||
|
||||
def test_manifest_does_not_contain_secrets():
|
||||
text = MANIFEST.read_text(encoding="utf-8")
|
||||
assert "sk_live_" not in text
|
||||
assert "api_key" not in text.lower()
|
||||
|
||||
|
||||
def test_rules_default_below_adjudication():
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
for rule in data["rules"]:
|
||||
assert rule["status"] in {
|
||||
"transcribed",
|
||||
"source_verified",
|
||||
"golden_verified",
|
||||
"engine_cross_checked",
|
||||
"case_calibrated",
|
||||
"blocked",
|
||||
}
|
||||
assert rule["status"] != "adjudication_enabled"
|
||||
assert rule["adjudication_enabled"] is False
|
||||
|
||||
|
||||
def test_screenshot_source_paths_are_located_and_hashed():
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
source = next(item for item in data["sources"] if item["id"] == "uploaded_screenshots_20260716")
|
||||
assert source["extraction_status"] == "located_and_hashed_ocr_blocked_tesseract_missing"
|
||||
assert len(source["paths"]) == 6
|
||||
assert all("/文件仓库/印度占星文章/260716/" in path for path in source["paths"])
|
||||
assert set(source["sha256"]) == {
|
||||
"IMG_3502.PNG",
|
||||
"IMG_3503.PNG",
|
||||
"IMG_3504.PNG",
|
||||
"IMG_3505.PNG",
|
||||
"IMG_3506.PNG",
|
||||
"IMG_3507.PNG",
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
from scripts import rangacharya
|
||||
|
||||
|
||||
SAMPLE_LONGS = {
|
||||
"Sun": 10.0,
|
||||
"Moon": 45.0,
|
||||
"Mars": 80.0,
|
||||
"Mercury": 110.0,
|
||||
"Jupiter": 145.0,
|
||||
"Venus": 200.0,
|
||||
"Saturn": 250.0,
|
||||
"Rahu": 300.0,
|
||||
"Ketu": 120.0,
|
||||
}
|
||||
|
||||
|
||||
def test_variant_result_is_experimental_and_not_for_adjudication():
|
||||
result = rangacharya.calc_rangacharya_variant(0, SAMPLE_LONGS)
|
||||
assert result["variant"] == "rangacharya"
|
||||
assert result["adjudication_enabled"] is False
|
||||
assert result["status"] == "experimental_not_for_adjudication"
|
||||
|
||||
|
||||
def test_variant_includes_core_sections():
|
||||
result = rangacharya.calc_rangacharya_variant(0, SAMPLE_LONGS)
|
||||
assert "source_status" in result
|
||||
assert "arudha_padas" in result
|
||||
assert "active_lagna" in result
|
||||
assert "effective_lagna" in result
|
||||
assert result["arudha_padas"]["AL"]["source_card_id"] == "rangacharya_core_arudha"
|
||||
assert result["arudha_padas"]["AL"]["source_card_status"] == "transcribed"
|
||||
assert result["active_lagna"]["source_card_id"] == "active_effective_lagna"
|
||||
assert result["effective_lagna"]["source_card_id"] == "active_effective_lagna"
|
||||
|
||||
|
||||
def test_diff_marks_algorithm_names():
|
||||
current = {"AL": {"sign": "Aries"}}
|
||||
variant = {"arudha_padas": {"AL": {"sign": "Taurus"}}}
|
||||
diff = rangacharya.diff_current_vs_rangacharya(current, variant)
|
||||
assert diff["current_algorithm"] == "current_jaimini"
|
||||
assert diff["variant_algorithm"] == "rangacharya"
|
||||
assert diff["differences"][0]["key"] == "AL.sign"
|
||||
Reference in New Issue
Block a user