sync(upstream): port astrologer branch d09feab7 (public-sync-20261107) calculation changes
Product 2026-11-07: sync the astrologer's working branch (ea2a9d65..d09feab7). - 0266b6ad: year lord picks by the native five-fold Panchavargiya only; a PyJHora score (research switch) is attached per candidate as external_oracle_comparison (observation_only_not_selection_input) and never selects. panchavargiya_selection_policy on the result; candidate_list and the reader-report candidate table carry the five components and raw total. The legacy Sun/Mars/Saturn table relabel is not applicable: calc_sodhita_av was already removed here (BUG-1209). - 8c4bff8a + a5b75886: Sodhita no longer votes in finance adjudication; the bridge keeps raw 2/11 scores with prediction_scoring_allowed false. - ba8e36ec: finance delivery inventory (raw, no vote), wrapped producer status counts as blocked in machine-evidence sections, transit section, report gate evidence_claims bindings, finance support review readiness. - 5962368d: dispositor chain dispositor_sign is the dispositor's own sign. - 8813c851: BCP observed at the reading date (bcp_reference, PL9 row). - d09feab7: nakshatra_advanced.calculate_stellar_jeeva_sareera and the nadi-stellar-roles CLI; specialized-jyotish SKILL.md, usage packs, ontology and gap scanner mirrored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# Stellar Jeeva/Sareera: bounded native calculation
|
||||
|
||||
## Scope and current-window exploration
|
||||
|
||||
Repository maintenance, not a personal interpretation or a customer export.
|
||||
Starting HEAD b88fdb3b was clean on codex/public-sync-20261107. Pre-work check
|
||||
passed, including external-adapter diagnostics and remote visibility. Seven
|
||||
worktrees were listed. Existing current-capability index, report authority,
|
||||
PL9 dashboard and error ledger were consulted. Governance reports only
|
||||
main_worktree_dirty during this implementation; that is not an adapter failure.
|
||||
Earlier whole-machine inventory is documented in knowledge_call_chain_followup_2026_11_07.md;
|
||||
it is not represented as a fresh full-content scan here. This round searched
|
||||
the native scripts, tests, specialized skill, review/extraction queues and the
|
||||
registered local stellar book. No mirror, frozen tree or personal report changed.
|
||||
|
||||
Reuse: find_nakshatra for half-open stellar boundaries; Jaimini module's
|
||||
traditional sign-owner table only (not its dual-owner/strength rules);
|
||||
existing Nadi review queue for discovery; native engine CLI for actual execution.
|
||||
|
||||
## Source record and conflict
|
||||
|
||||
Local corpus registration:
|
||||
`Nadi_Stellar Effects -- Jyotish -- 2022 -- f8eef571147711ee4eb4479a35bd2428 -- Anna’s Archive.md`.
|
||||
Text SHA256: `f30db43d842a9484316d6608f6385dfabe7232265469e67938f42459e25c4b42`.
|
||||
The filename year is not a verified publication edition. Chapter 4 is the
|
||||
Jeeva/Sareera chapter in this Kalamsa-Gunas/Meena source. The book cites
|
||||
N. V. Raghava Chary, Practical Stellar Astrology, second edition 2004,
|
||||
pp108–111; that original edition has NOT been independently checked.
|
||||
|
||||
| Topic | PDF page | Printed page | Corpus line locator |
|
||||
|---|---:|---:|---:|
|
||||
| Four starting rules | 87 | 84 | 4513 onward |
|
||||
| Conflicting exalted-Venus example | 89 | 86 | 4626 onward |
|
||||
| Detailed controlling-planet hierarchy and Saturn exception | 92 | 89 | 4800 onward |
|
||||
| Comparative summary with different qualifications | 93–94 | 90–91 | 4882 onward |
|
||||
| Anonymous structural table | 95–99 | 92–96 | 4979 onward |
|
||||
|
||||
Used search_jyotish_books.py with the registered filename, query Jeeva and
|
||||
read-unit 92. Visually inspected original PDF pages 89 and 92. Page89 actually
|
||||
names Mercury then Jupiter as Sareera in the same example; it is a printed
|
||||
conflict, not merely OCR. No copyrighted source pages/text are added to Git.
|
||||
|
||||
The explicit runtime profile follows the detailed procedure on printed p89:
|
||||
original Saturn in Anuradha retains control; otherwise sign owner, exaltation,
|
||||
stellar Digbala, own-star among Digbala candidates, listed strength order,
|
||||
then retain the original seed. It does not silently substitute the later
|
||||
comparative list's broader own-star/debility qualifications. The own-star
|
||||
Sareera fallback applies to the final Jeeva, as the printed pp93–95 table
|
||||
demonstrates even when the initial bhava lord is different.
|
||||
|
||||
## Callable contract
|
||||
|
||||
`scripts.nakshatra_advanced.calculate_stellar_jeeva_sareera` accepts:
|
||||
|
||||
- Exactly nine already-sidereal planetary longitudes, one explicit bhava lord,
|
||||
explicit ayanamsa label and mean/true node mode. It does not derive birth data.
|
||||
- Optional stellar_digbala boolean map. Missing values are not false. Numeric
|
||||
Shadbala scores are rejected. These flags remain caller-supplied assumptions.
|
||||
- Optional nodal_exaltation_signs: explicit zero-based sign or null for each node.
|
||||
No cross-school nodal exaltation is silently adopted. If this matters to an
|
||||
unresolved group and is absent, that branch is blocked.
|
||||
|
||||
Native CLI: `python3 scripts/jyotish_engine.py nadi-stellar-roles --request <local.json>`.
|
||||
The local JSON fields are the function's argument names. It prints JSON without
|
||||
writing charts, profiles or exports. Roles, selected profile, source identity,
|
||||
stage seeds/companions, decision reasons and missing inputs remain visible.
|
||||
Status is executed or blocked; evidence is parameter_sensitive;
|
||||
external_verified and prediction_scoring_allowed remain false.
|
||||
|
||||
The existing Nadi review queue now points to the real function. A queue listing
|
||||
still says not_executed and does not approve a rule or bypass admission.
|
||||
No customer frozen pin, annual score, finance score, Dasha date or interpretation
|
||||
is changed. No automatic forecasting use is claimed.
|
||||
|
||||
## External cross-check, not new rule admission
|
||||
|
||||
- [Practitioner article, 2012-02-28](https://srigurucriticalcareastrologer.blogspot.com/2012/02/nadi-jyothisha-jeeva-and-sareera.html):
|
||||
own-star and substitution examples overlap the existing book. Its Venus example
|
||||
names Mercury without the following Jupiter sentence. Corroboration supports
|
||||
an editorial-error hypothesis, not a verified erratum or predictive validation.
|
||||
- [Iranganti Rangacharya article](https://saptarishisshop.com/jeeva-sareera-theory-of-prediction-i-by-iranganti-rangacharya/):
|
||||
distinct same-name tradition, not admitted into this Meena profile.
|
||||
|
||||
No new web-only rule enters runtime; these are source-comparison/discovery URLs.
|
||||
No community testimony or repeated example is treated as independent calibration.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
269 related tests passed, including native CLI execution, seven traditional
|
||||
planet roles from the anonymous source table under explicit stellar assumptions,
|
||||
own-star fallback, owner/exaltation precedence, seed-specific Saturn exception,
|
||||
missing inputs, invalid numbers and Nadi queue admission boundaries.
|
||||
That table replay is not an independent oracle; the uncertain Mars OCR minute
|
||||
is represented by a same-star/sign value and is not a precise longitude check.
|
||||
|
||||
Still open: source-specific automatic Nakshatra Digbala and its aspect rules;
|
||||
adjudication of the comparative-list differences and nodal exaltations;
|
||||
independent numerical cases; predictive calibration; optional report routing.
|
||||
This is source-bounded structural execution, not global closure or permission
|
||||
to turn these roles alone into financial, medical or event predictions.
|
||||
+22
-10
@@ -2090,18 +2090,25 @@ def _derive_sodhita_finance_support(ashtakavarga: Any, asc_sign: Optional[str])
|
||||
sign_to_score[str(sign)] = float(score)
|
||||
|
||||
raw_scores: Dict[str, Any] = {}
|
||||
numeric_scores: Dict[int, float] = {}
|
||||
for house in (2, 11):
|
||||
sign = _SIGNS[(asc_idx + house - 1) % 12]
|
||||
if sign in sign_to_score:
|
||||
numeric_scores[house] = sign_to_score[sign]
|
||||
value = sign_to_score[sign]
|
||||
raw_scores[str(house)] = int(value) if value.is_integer() else value
|
||||
|
||||
base["raw_scores"] = raw_scores
|
||||
if len(numeric_scores) == 2 and max(numeric_scores.values()) <= 19:
|
||||
base["level"] = "obstructive"
|
||||
base["signals"] = ["sodhita_wealth_friction"]
|
||||
producer = _safe_get(ashtakavarga, "sodhita") or {}
|
||||
# Upstream a5b75886: no source-adjudicated finance threshold exists for this
|
||||
# bridge. Caller method/permission labels cannot make the legacy <=19 penalty valid.
|
||||
known_legacy = (
|
||||
producer.get("method_id") == "legacy_malefic_contribution_subtraction_v1"
|
||||
or producer.get("method") == "Sodhita Ashtakavarga (BPHS标准)"
|
||||
)
|
||||
base["status"] = producer.get("status") or (
|
||||
"legacy_reference_only" if known_legacy else "method_unverified"
|
||||
)
|
||||
base["prediction_scoring_allowed"] = False
|
||||
base["reason"] = "unadjudicated_contribution_subtraction_not_wealth_evidence"
|
||||
return base
|
||||
|
||||
|
||||
@@ -2610,9 +2617,9 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
pav_finance_support = present.get("pav_finance_support") or {}
|
||||
if pav_finance_support.get("level") == "supportive":
|
||||
score += 2
|
||||
sodhita_finance_support = present.get("sodhita_finance_support") or {}
|
||||
if sodhita_finance_support.get("level") == "obstructive":
|
||||
score -= 2
|
||||
# Upstream a5b75886: Sodhita packets remain available as raw evidence,
|
||||
# never a finance vote. No admitted threshold/profile exists, including
|
||||
# for historical caches.
|
||||
kakshya_finance_support = present.get("kakshya_finance_support") or {}
|
||||
if kakshya_finance_support.get("level") == "supportive":
|
||||
score += 2
|
||||
@@ -2680,8 +2687,6 @@ def _derive_event_judgement(route: str, present: Dict[str, Any], missing: List[s
|
||||
secondary_context.append("functional_benefic_malefic_used")
|
||||
if pav_finance_support.get("level") == "supportive":
|
||||
secondary_context.append("pav_finance_support")
|
||||
if sodhita_finance_support.get("level") == "obstructive":
|
||||
secondary_context.append("sodhita_wealth_friction")
|
||||
if kakshya_finance_support.get("level") == "supportive":
|
||||
secondary_context.append("kakshya_finance_support")
|
||||
elif kakshya_finance_support.get("level") == "obstructive":
|
||||
@@ -4005,6 +4010,13 @@ def _collect_strict_evidence(route: str, result: Dict[str, Any]) -> Dict[str, An
|
||||
"plus at least one wealth-related convergence domain."
|
||||
),
|
||||
}
|
||||
# Upstream ba8e36ec: optional existing producers are delivered as raw
|
||||
# inventory only; they never vote or fill required evidence.
|
||||
try:
|
||||
from scripts.finance_astrology_support_review import build_finance_delivery_inventory
|
||||
except ImportError: # pragma: no cover - scripts/ on sys.path
|
||||
from finance_astrology_support_review import build_finance_delivery_inventory
|
||||
strict["finance_delivery_inventory"] = build_finance_delivery_inventory(modules)
|
||||
strict["official_primary_evidence"] = _build_official_primary_evidence(route, present)
|
||||
strict["local_supplemental_evidence"] = _build_local_supplemental_evidence(route, present)
|
||||
strict["fallback_used"], strict["blocked_items"] = _build_fallback_and_blocked(
|
||||
|
||||
@@ -707,12 +707,13 @@
|
||||
"calculation_status": "source_bounded_wired",
|
||||
"doctrine_status": "usage_pack_present",
|
||||
"variant_status": "profile_inventory_present",
|
||||
"variant_profile_ids": ["niryaana_shoola_goel_profile", "niryaana_shoola_raman_profile", "niryaana_shoola_pyjhora_observation_profile"],
|
||||
"variant_profile_ids": ["pvr_niryaana_shoola_v1", "niryaana_shoola_pyjhora_observation_profile"],
|
||||
"routing_status": "skill_linked",
|
||||
"report_status": "source_bounded_wired",
|
||||
"local_paths": ["scripts/niryaana_shoola_dasha.py", "scripts/niryaana_shoola_family_contract.py"],
|
||||
"report_paths": ["jyotish_engine.worksheets.timing_and_predictive_systems.dasha_master_pack.families.niryaana_shoola", "references/oracle/pl9_p92_p103_field_audit_2026_09_11.json"],
|
||||
"producer_contract": {"entrypoint": "scripts.niryaana_shoola_dasha.calculate_niryaana_shoola_dasha", "maturity": "source_bounded_wired", "verification_scope": "stronger Lagna/seventh selection, explicit tie-break, named Goel/Raman/PyJHora duration profiles and MD/AD/PD boundaries", "claim_boundary": "Named profile support only; worksheet path is wired but parameter-sensitive parity remains open."}
|
||||
"producer_contract": {"entrypoint": "scripts.niryaana_shoola_dasha.calculate_niryaana_shoola_dasha", "maturity": "source_bounded_wired", "verification_scope": "PVR second/eighth ayur-strength selection with unresolved ties blocked; movable/fixed/dual 7/8/9 calendar-year MD; external observation separate", "claim_boundary": "Named MD profile only, longevity-domain research rather than relationship timing; AD/PD source rule and same-profile external parity remain open."},
|
||||
"next_required": ["source-backed AD/PD rule and row-level replay", "independent same-profile MD case; no predictive or global admission"]
|
||||
},
|
||||
"kalachakra_dasha": {"calculation_status":"source_bounded_wired","doctrine_status":"usage_pack_present","variant_status":"profile_inventory_present","variant_profile_ids":["kalachakra_native_recursive_profile","kalachakra_pl9_pages_83_88_boundary_profile","kalachakra_pyjhora_observation_profile"],"routing_status":"skill_linked","report_status":"source_bounded_wired","local_paths":["scripts/kalachakra_dasha.py","scripts/kalachakra_pl9_report_boundary_packet.py"],"report_paths":["jyotish_engine.worksheets.timing_and_predictive_systems.dasha_master_pack.families.kala_chakra","kalachakra_pl9_report_boundary_packet.boundary_rows","references/oracle/kalachakra_pl9_p83_88_structural_contract_2026_08_11.json"],"producer_contract":{"entrypoint":"scripts.kalachakra_dasha.calculate_kalachakra_dasha","maturity":"source_bounded_wired","verification_scope":"savya/apasavya selection, balance, Jeeva/Deha and recursive MD/AD/PD with PL9 conflict packet","claim_boundary":"Conflict-labeled Kala Chakra output; PL9 continuation and external parity remain unclosed."}},
|
||||
"lagna_kendradi_moola_dasha": {"calculation_status":"source_bounded_wired","doctrine_status":"usage_pack_present","variant_status":"profile_inventory_present","variant_profile_ids":["lagna_kendradi_explicit_seed_profile","lagna_kendradi_pyjhora_observation_profile"],"routing_status":"skill_linked","report_status":"source_bounded_wired","local_paths":["scripts/lagna_kendradi_dasha.py","scripts/lagna_kendradi_family_contract.py"],"report_paths":["jyotish_engine.worksheets.timing_and_predictive_systems.dasha_master_pack.families.lagna_kendradi","references/oracle/pl9_p92_p103_field_audit_2026_09_11.json"],"producer_contract":{"entrypoint":"scripts.lagna_kendradi_dasha.calculate_lagna_kendradi_dasha","maturity":"source_bounded_wired","verification_scope":"explicit Lagna seed, Kendra/Panaphara/Apoklima group order and profile arbitration","claim_boundary":"Explicit profile producer; automatic selector and source parity remain open."}},
|
||||
|
||||
@@ -136,7 +136,7 @@ def calc_pada_dasha_marriage_timing(birth_moon_lon, birth_date_jd,
|
||||
return analysis
|
||||
|
||||
def bhrigu_pada_dasha_full_report(birth_moon_lon, birth_date_jd,
|
||||
d9_7lord_sign=None, d9_planets=None):
|
||||
d9_7lord_sign=None, d9_planets=None, *, target_date_jd=None):
|
||||
"""
|
||||
Bhrigu Pada Dasha 完整报告 v7.0
|
||||
|
||||
@@ -147,6 +147,7 @@ def bhrigu_pada_dasha_full_report(birth_moon_lon, birth_date_jd,
|
||||
- birth_date_jd: 出生 JD
|
||||
- d9_7lord_sign: D9 7宫主星座 (0-11)
|
||||
- d9_planets: D9 行星数据 {name: longitude}
|
||||
- target_date_jd: BCP 观测 JD;省略时只输出明确标记的出生时点(上游 8813c851)
|
||||
|
||||
Returns:
|
||||
- dict: 完整报告
|
||||
@@ -221,7 +222,12 @@ def bhrigu_pada_dasha_full_report(birth_moon_lon, birth_date_jd,
|
||||
report['d9_verification']['window_details'] = window_details
|
||||
|
||||
# BCP周期整合
|
||||
report['bcp_cycle'] = calc_bcp_cycle(birth_moon_lon, birth_date_jd, birth_date_jd)
|
||||
bcp_target_jd = birth_date_jd if target_date_jd is None else target_date_jd
|
||||
report['bcp_cycle'] = calc_bcp_cycle(birth_moon_lon, birth_date_jd, bcp_target_jd)
|
||||
report['bcp_reference'] = {
|
||||
'target_jd': bcp_target_jd,
|
||||
'scope': 'birth_epoch_default' if target_date_jd is None else 'explicit_target',
|
||||
}
|
||||
|
||||
return report
|
||||
|
||||
|
||||
@@ -20,9 +20,55 @@ FINANCE_PRIORITY_GAPS = [
|
||||
|
||||
|
||||
def _present(value: Any) -> str:
|
||||
if isinstance(value, dict) and str(value.get("status", "")).lower() in {
|
||||
"blocked", "missing", "error", "failed", "not_available_yet", "not_executed", "not_applicable"
|
||||
}:
|
||||
return "blocked"
|
||||
return "present" if bool(value) else "missing"
|
||||
|
||||
|
||||
FINANCE_EXISTING_MODULES = {
|
||||
"argala": "scripts/argala.py",
|
||||
"dispositor_chains": "scripts/jyotish_engine.py",
|
||||
"tajika": "scripts/solar_return.py",
|
||||
"bhrigu_pada_dasha": "scripts/bhrigu_pada_dasha.py",
|
||||
"sudasa": "scripts/sudasa.py",
|
||||
}
|
||||
|
||||
|
||||
def build_finance_delivery_inventory(modules: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Preserve existing optional data without calling presence execution or a vote."""
|
||||
from copy import deepcopy
|
||||
|
||||
rows = []
|
||||
for name, producer in FINANCE_EXISTING_MODULES.items():
|
||||
value = modules.get(name)
|
||||
presence = _present(value)
|
||||
rows.append({
|
||||
"technique": name, "producer": producer,
|
||||
"source_path": "modules." + name,
|
||||
"delivery_status": {"present": "payload_delivered", "missing": "not_delivered",
|
||||
"blocked": "producer_status_blocked"}[presence],
|
||||
"source_status": value.get("status") if isinstance(value, dict) else None,
|
||||
"execution_status": "not_verified_by_inventory",
|
||||
"raw": deepcopy(value),
|
||||
})
|
||||
return {
|
||||
"rows": rows,
|
||||
"claim_boundary": "delivery_inventory_only_not_execution_or_prediction_validation",
|
||||
"missing_means": "not_delivered_in_this_packet_not_missing_from_repository",
|
||||
"presentation_order": ["executive_summary", "thematic_narrative", "evidence_appendix"],
|
||||
"inference_constraints": [
|
||||
"Strength is not outcome valence; SAV and Bhava Bala do not prove retention.",
|
||||
"Repeated planet or house evidence is not independent confirmation.",
|
||||
"Drig is not a wealth vote without a named source-specific domain rule.",
|
||||
"Unknown birth precision requires sensitivity evidence for narrow windows.",
|
||||
"Retain varga methods, full period boundaries and dispositor chains.",
|
||||
"Repeated model agreement is not event validation or calibrated probability.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def build_finance_astrology_support_review(
|
||||
*,
|
||||
route_packet: dict[str, Any] | None,
|
||||
@@ -48,7 +94,7 @@ def build_finance_astrology_support_review(
|
||||
"d2": _present(sections.get("D2")),
|
||||
"d11": _present(machine_evidence_packet.get("D11") or sections.get("D11")),
|
||||
"vimshottari": _present(sections.get("dasha_boundaries")),
|
||||
"narayana": _present(runtime_evidence_log.get("cross_system_arbitration")),
|
||||
"narayana": _present(sections.get("narayana") or sections.get("narayana_dasha")),
|
||||
"shadbala": _present(sections.get("shadbala")),
|
||||
"ashtakavarga": _present(sections.get("ashtakavarga")),
|
||||
"transit": _present(sections.get("transit")),
|
||||
@@ -67,13 +113,19 @@ def build_finance_astrology_support_review(
|
||||
],
|
||||
}
|
||||
|
||||
finance_surface["status"] = "used" if route in {"finance", "wealth"} else "not_applicable"
|
||||
finance_surface["missing_evidence"] = [
|
||||
key for key, state in finance_surface["coverage"].items() if state != "present"
|
||||
]
|
||||
finance_surface["readiness"] = (
|
||||
"not_applicable" if route not in {"finance", "wealth"} else
|
||||
"incomplete" if finance_surface["missing_evidence"] else "payloads_present_execution_unverified"
|
||||
)
|
||||
finance_surface["audit_rows"] = [
|
||||
{
|
||||
"technique": "Finance Astrology Support Review",
|
||||
"status": finance_surface["status"],
|
||||
"used": finance_surface["status"] == "used",
|
||||
"effect_on_confidence": "finance_route_support_ready" if finance_surface["status"] == "used" else "not_applicable",
|
||||
"effect_on_confidence": finance_surface["readiness"],
|
||||
}
|
||||
]
|
||||
return finance_surface
|
||||
|
||||
@@ -140,6 +140,45 @@ def _promotion_status(blocking_reasons: list[str], review_reasons: list[str], wa
|
||||
return "passed"
|
||||
|
||||
|
||||
def evaluate_evidence_claims(packet: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Validate explicit claim bindings, not arbitrary free-text astrological prose.
|
||||
|
||||
Each claim has a path (list of dictionary keys/list indices) and a value.
|
||||
Model-declared 'used' or a repeated answer never counts as a receipt.
|
||||
"""
|
||||
claims = packet.get("evidence_claims")
|
||||
if claims is None or claims == []:
|
||||
return {"status": "not_supplied", "checked": 0, "errors": [],
|
||||
"boundary": "free_text_not_semantically_verified"}
|
||||
if not isinstance(claims, list):
|
||||
return {"status": "blocked", "checked": 0, "errors": ["claims_not_list"]}
|
||||
errors = []
|
||||
for i, claim in enumerate(claims):
|
||||
if not isinstance(claim, dict) or "value" not in claim:
|
||||
errors.append(f"claim_{i}:invalid_binding")
|
||||
continue
|
||||
path = claim.get("path")
|
||||
if not isinstance(path, list) or not path or path[0] == "evidence_claims":
|
||||
errors.append(f"claim_{i}:invalid_path")
|
||||
continue
|
||||
value = packet
|
||||
try:
|
||||
for key in path:
|
||||
if isinstance(value, dict) and isinstance(key, str):
|
||||
value = value[key]
|
||||
elif isinstance(value, list) and type(key) is int and key >= 0:
|
||||
value = value[key]
|
||||
else:
|
||||
raise KeyError("invalid_path_component")
|
||||
except (KeyError, IndexError, TypeError):
|
||||
errors.append(f"claim_{i}:source_missing")
|
||||
continue
|
||||
if type(value) is not type(claim["value"]) or value != claim["value"]:
|
||||
errors.append(f"claim_{i}:value_mismatch")
|
||||
return {"status": "blocked" if errors else "bindings_match", "checked": len(claims),
|
||||
"errors": errors, "boundary": "bound_values_only_not_formula_or_prediction_validation"}
|
||||
|
||||
|
||||
def evaluate_full_report(packet: dict[str, Any], rendered_markdown: str | None = None) -> dict[str, Any]:
|
||||
"""Evaluate an assembled report without mutating calculation or report content."""
|
||||
packet = _as_dict(packet)
|
||||
@@ -147,6 +186,9 @@ def evaluate_full_report(packet: dict[str, Any], rendered_markdown: str | None =
|
||||
blocking_reasons: list[str] = []
|
||||
review_reasons: list[str] = []
|
||||
warning_reasons: list[str] = []
|
||||
claim_validation = evaluate_evidence_claims(packet)
|
||||
if claim_validation["status"] == "blocked":
|
||||
blocking_reasons.append("evidence_claim_bindings_invalid")
|
||||
|
||||
source_schema = str(packet.get("schema") or "")
|
||||
if source_schema != REPORT_SCHEMA:
|
||||
@@ -557,6 +599,7 @@ def evaluate_full_report(packet: dict[str, Any], rendered_markdown: str | None =
|
||||
"status": status,
|
||||
"checks": checks,
|
||||
"blocking_reasons": blocking_reasons,
|
||||
"evidence_claim_validation": claim_validation,
|
||||
"review_reasons": review_reasons,
|
||||
"warning_reasons": warning_reasons,
|
||||
"professional_coverage_manifest": professional_coverage_manifest,
|
||||
|
||||
@@ -592,12 +592,14 @@ def calc_dispositor_chain(planet_name, planets_data, max_depth=12):
|
||||
break
|
||||
sign = pdata['sign']
|
||||
dispositor = SIGN_LORDS.get(sign, '')
|
||||
# Upstream 5962368d: the dispositor's own location, not the sign it owns.
|
||||
dispositor_data = planets_data.get(dispositor)
|
||||
chain.append({
|
||||
'planet': current_planet,
|
||||
'sign': sign,
|
||||
'sign_cn': SIGNS_CN.get(sign, ''),
|
||||
'dispositor': dispositor,
|
||||
'dispositor_sign': sign, # 定位星所在的星座(即当前行星所在星座)
|
||||
'dispositor_sign': dispositor_data.get('sign') if isinstance(dispositor_data, dict) else None,
|
||||
})
|
||||
if not dispositor:
|
||||
break
|
||||
@@ -9934,7 +9936,7 @@ def render_pl9_markdown(packet: dict) -> str:
|
||||
raw_timing_rows.append((
|
||||
'Bhrigu Pada / BCP',
|
||||
bhrigu_raw.get('status', 'blocked'),
|
||||
f"出生月亮={birth_moon.get('longitude', '-')}°;序列行数={len(bhrigu_raw.get('dasha_sequence', []))}",
|
||||
f"出生月亮={birth_moon.get('longitude', '-')}°;序列行数={len(bhrigu_raw.get('dasha_sequence', []))};BCP观测日期={bhrigu_raw.get('bcp_reference_date', '未指定;核对原始时点')}",
|
||||
'raw_full_reading.modules.bhrigu_pada_dasha',
|
||||
))
|
||||
pac_raw = timing_sheet.get('double_transit_pac') if isinstance(timing_sheet.get('double_transit_pac'), dict) else {}
|
||||
@@ -14753,6 +14755,14 @@ def cmd_nakshatra_dasha(args):
|
||||
return _build_response_envelope('nakshatra-dasha', _impl(args), args=args, execution_status='executed')
|
||||
|
||||
|
||||
def cmd_nadi_stellar_roles(args):
|
||||
"""Upstream d09feab7: explicit Meena Jeeva/Sareera structure; no prediction or scoring."""
|
||||
from nakshatra_advanced import calculate_stellar_jeeva_sareera
|
||||
|
||||
request = json.loads(Path(args.request).read_text(encoding='utf-8'))
|
||||
return calculate_stellar_jeeva_sareera(**request)
|
||||
|
||||
|
||||
def cmd_nakshatra_full(args):
|
||||
from cmd_nakshatra_adv import cmd_nakshatra_full as _impl
|
||||
return _build_response_envelope('nakshatra-full', _impl(args), args=args, execution_status='executed')
|
||||
@@ -16246,11 +16256,17 @@ def cmd_full_reading(args):
|
||||
d9_seventh_lord_data = d9_data.get(d9_seventh_lord, {}) if isinstance(d9_data, dict) else {}
|
||||
if isinstance(d9_seventh_lord_data, dict) and d9_seventh_lord_data.get('sign') in SIGNS:
|
||||
d9_7lord_sign = SIGNS.index(d9_seventh_lord_data['sign'])
|
||||
# Upstream 8813c851: BCP is observed at the reading date, not the birth epoch.
|
||||
bcp_reference_date = getattr(args, 'transit_date', None) or getattr(args, 'today', None) or datetime.now().strftime('%Y-%m-%d')
|
||||
bcp_year, bcp_month, bcp_day = map(int, bcp_reference_date.split('-'))
|
||||
bcp_target_jd = swe.julday(bcp_year, bcp_month, bcp_day, 12.0 - args.tz)
|
||||
bpd_result = bhrigu_pada_dasha_full_report(
|
||||
moon_lon, birth_jd,
|
||||
d9_7lord_sign=d9_7lord_sign,
|
||||
d9_planets=d9_planets,
|
||||
target_date_jd=bcp_target_jd,
|
||||
)
|
||||
bpd_result['bcp_reference_date'] = bcp_reference_date
|
||||
bpd_result['status'] = 'parameter_sensitive'
|
||||
bpd_result['claim_boundary'] = (
|
||||
'Bhrigu Pada / BCP is a source-limited approximate progression family. '
|
||||
@@ -18796,6 +18812,10 @@ def main():
|
||||
p.add_argument('--age', type=float, default=None, help='当前年龄')
|
||||
p.add_argument('--transit-date', default=None, help='过境日期 YYYY-MM-DD(默认今天)')
|
||||
|
||||
# 18.7 nadi-stellar-roles (upstream d09feab7)
|
||||
p = sub.add_parser('nadi-stellar-roles', help='显式 Meena Jeeva/Sareera 结构计算;不生成预测或评分')
|
||||
p.add_argument('--request', required=True, help='本地 JSON:九星恒星经度、宫主、岁差、交点及可选星宿方向力')
|
||||
|
||||
# 19. argala (v3.7新增)
|
||||
p = sub.add_parser('argala', help='Argala门闩系统')
|
||||
_add_chart_args(p)
|
||||
@@ -18980,7 +19000,7 @@ def main():
|
||||
'validate': cmd_validate, 'audit': cmd_audit, 'report': cmd_report,
|
||||
'varga-full': cmd_varga_full, 'aspects': cmd_aspects, 'jaimini': cmd_jaimini,
|
||||
'nakshatra-adv': cmd_nakshatra_adv, 'nakshatra-dasha': cmd_nakshatra_dasha,
|
||||
'nakshatra-full': cmd_nakshatra_full, 'argala': cmd_argala, 'tajika': cmd_tajika,
|
||||
'nakshatra-full': cmd_nakshatra_full, 'nadi-stellar-roles': cmd_nadi_stellar_roles, 'argala': cmd_argala, 'tajika': cmd_tajika,
|
||||
'marriage-match': cmd_marriage_match, 'solar-return': cmd_solar_return,
|
||||
'narayana-dasha': cmd_narayana_dasha, 'muhurta': cmd_muhurta,
|
||||
'full-reading': cmd_full_reading, 'pl9-export': cmd_pl9_export, 'prashna': cmd_prashna,
|
||||
|
||||
@@ -111,6 +111,115 @@ def find_nakshatra(longitude: float) -> Dict:
|
||||
}
|
||||
|
||||
|
||||
def calculate_stellar_jeeva_sareera(
|
||||
positions: Dict, *, bhava_lord: str, ayanamsa: str, node_mode: str,
|
||||
stellar_digbala: Optional[Dict] = None,
|
||||
nodal_exaltation_signs: Optional[Dict] = None,
|
||||
) -> Dict:
|
||||
"""Structural Meena classification, explicit printed-p89 procedure only.
|
||||
|
||||
Input longitudes are already sidereal; this function does not convert them.
|
||||
Stellar Digbala is a supplied boolean relationship result, NOT Shadbala.
|
||||
Nodal exaltations have no implicit default. Unknown comparisons stay blocked.
|
||||
See docs/research/stellar_jeeva_sareera_2026_11_07.md for source conflicts.
|
||||
"""
|
||||
if __package__:
|
||||
from .jaimini import SIGNS, SIGN_LORDS
|
||||
else:
|
||||
from jaimini import SIGNS, SIGN_LORDS
|
||||
planets = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus",
|
||||
"Saturn", "Rahu", "Ketu")
|
||||
if not isinstance(positions, dict) or set(positions) != set(planets):
|
||||
raise ValueError("Supply exactly nine planetary sidereal longitudes")
|
||||
for value in positions.values():
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value):
|
||||
raise ValueError("Planetary longitudes must be finite numbers")
|
||||
if bhava_lord not in planets or not isinstance(ayanamsa, str) or not ayanamsa.strip():
|
||||
raise ValueError("Explicit bhava_lord and ayanamsa are required")
|
||||
if node_mode not in {"mean", "true"}:
|
||||
raise ValueError("node_mode must be mean or true")
|
||||
strengths = {} if stellar_digbala is None else stellar_digbala
|
||||
if not isinstance(strengths, dict) or any(
|
||||
p not in planets or type(v) is not bool for p, v in strengths.items()
|
||||
):
|
||||
raise ValueError("stellar_digbala requires planetary booleans, not strength scores")
|
||||
nodes = {} if nodal_exaltation_signs is None else nodal_exaltation_signs
|
||||
if not isinstance(nodes, dict) or any(
|
||||
p not in {"Rahu", "Ketu"} or (v is not None and (type(v) is not int or not 0 <= v < 12))
|
||||
for p, v in nodes.items()
|
||||
):
|
||||
raise ValueError("Explicit nodal exaltation: sign index 0..11 or None")
|
||||
signs = {p: int(v % 360 // 30) for p, v in positions.items()}
|
||||
stars = {p: find_nakshatra(v) for p, v in positions.items()}
|
||||
exaltation = dict(Sun=0, Moon=1, Mars=9, Mercury=5, Jupiter=3, Venus=11, Saturn=6)
|
||||
exaltation.update(nodes)
|
||||
order = {p: i for i, p in enumerate(("Rahu", "Ketu", "Sun", "Moon", "Venus",
|
||||
"Jupiter", "Saturn", "Mars", "Mercury"))}
|
||||
trace = []
|
||||
|
||||
def choose(seed, stage):
|
||||
group = [p for p in planets if signs[p] == signs[seed]]
|
||||
row = {"stage": stage, "seed": seed, "companions": group}
|
||||
trace.append(row)
|
||||
|
||||
def finish(selected, reason):
|
||||
row.update(selected=selected, reason=reason)
|
||||
return selected
|
||||
|
||||
if seed == "Saturn" and signs[seed] == 7 and stars[seed]["nakshatra_idx"] == 16:
|
||||
return finish(seed, "original_saturn_anuradha_exception")
|
||||
if len(group) == 1:
|
||||
return finish(seed, "alone")
|
||||
owner = SIGN_LORDS[SIGNS[signs[seed]]]
|
||||
if owner in group:
|
||||
return finish(owner, "sign_owner")
|
||||
unknown_nodes = [p for p in group if p in {"Rahu", "Ketu"} and p not in nodes]
|
||||
if unknown_nodes:
|
||||
row["missing"] = unknown_nodes
|
||||
return finish(None, "nodal_exaltation_profile_required")
|
||||
exalted = [p for p in group if exaltation.get(p) == signs[p]]
|
||||
if len(exalted) > 1:
|
||||
return finish(None, "multiple_exalted_comparison_unresolved")
|
||||
if exalted:
|
||||
return finish(exalted[0], "exalted")
|
||||
missing = [p for p in group if p not in strengths]
|
||||
if missing:
|
||||
row["missing"] = missing
|
||||
return finish(None, "stellar_digbala_required")
|
||||
directional = [p for p in group if strengths[p]]
|
||||
if not directional:
|
||||
return finish(seed, "no_stronger_companion")
|
||||
own_star = [p for p in directional if stars[p]["nakshatra_lord"] == p]
|
||||
return finish(min(own_star or directional, key=order.get),
|
||||
"stellar_digbala_own_star" if own_star else "stellar_digbala_order")
|
||||
|
||||
jeeva = choose(stars[bhava_lord]["nakshatra_lord"], "jeeva")
|
||||
sareera = None
|
||||
if jeeva is not None:
|
||||
seed = stars[jeeva]["nakshatra_lord"]
|
||||
if seed == jeeva:
|
||||
seed = SIGN_LORDS[SIGNS[signs[jeeva]]]
|
||||
sareera = choose(seed, "sareera")
|
||||
return {
|
||||
"schema": "jyotish.stellar_jeeva_sareera.v1",
|
||||
"method": "meena_stellar_detailed_procedure_printed_p89",
|
||||
"status": "executed" if sareera is not None else "blocked",
|
||||
"evidence_grade": "parameter_sensitive",
|
||||
"prediction_scoring_allowed": False,
|
||||
"external_verified": False,
|
||||
"profile": {"ayanamsa": ayanamsa, "node_mode": node_mode,
|
||||
"stellar_digbala": dict(strengths), "nodal_exaltation_signs": dict(nodes)},
|
||||
"bhava_lord": bhava_lord, "jeeva": jeeva, "sareera": sareera, "trace": trace,
|
||||
"source": {"corpus_file": "Nadi_Stellar Effects -- Jyotish -- 2022 -- f8eef571147711ee4eb4479a35bd2428 -- Anna’s Archive.md",
|
||||
"text_sha256": "f30db43d842a9484316d6608f6385dfabe7232265469e67938f42459e25c4b42",
|
||||
"pdf_pages": [87, 89, 90, 92, 96], "printed_pages": [84, 86, 87, 89, 93]},
|
||||
"boundaries": ["Structural roles only; no event, date or outcome inference",
|
||||
"Printed p86 example names two different Sareera planets",
|
||||
"Printed pp90-91 comparative list is not silently merged into p89",
|
||||
"Supplied stellar Digbala and nodal exaltation require separate source review"],
|
||||
}
|
||||
|
||||
|
||||
def calc_tara_bala(moon_nak_idx: int, target_nak_idx: int) -> Dict:
|
||||
"""
|
||||
Tara Bala计算:月亮星宿到目标星宿的9星宿循环关系
|
||||
|
||||
@@ -2439,15 +2439,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
lines.append('')
|
||||
if candidates:
|
||||
lines.extend([
|
||||
'| Candidate source | Planet | House | Sign | Panchavargiya score |',
|
||||
'|------------------|--------|-------|------|---------------------|',
|
||||
# Upstream 0266b6ad: the selecting score is the native five-fold
|
||||
# profile; its five components are shown beside it.
|
||||
'| Candidate source | Planet | House | Sign | Graha | Uchcha | Hudda | Drekkana | Navamsha | Raw (of 80) | Panchavargiya score |',
|
||||
'|------------------|--------|-------|------|-------|--------|-------|----------|----------|-------------|---------------------|',
|
||||
])
|
||||
for row in candidates:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
parts = _dict(row.get('panchavargiya_components'))
|
||||
lines.append(
|
||||
f"| {_cell(row.get('source'))} | {_cell(_token(row.get('planet')))} | {_cell(row.get('planet_house'))} | "
|
||||
f"{_cell(_token(row.get('planet_sign')))} | {_cell(row.get('panchavargiya_score'))} |"
|
||||
f"{_cell(_token(row.get('planet_sign')))} | "
|
||||
+ ''.join(f"{_cell(parts.get(key))} | " for key in ('graha_bala', 'uchcha_bala', 'hudda_bala', 'drekkana_bala', 'navamsha_bala'))
|
||||
+ f"{_cell(row.get('panchavargiya_raw_total'))} | {_cell(row.get('panchavargiya_score'))} |"
|
||||
)
|
||||
lines.append('')
|
||||
planet_longitudes = _dict(annual_chart_data.get('planet_longitudes'))
|
||||
|
||||
+11
-2
@@ -313,6 +313,7 @@ def calc_panchadhikari_year_lord(
|
||||
'panchavargiya_raw_total': pvb.get('raw_total'),
|
||||
'panchavargiya_engine': pvb['engine'],
|
||||
'panchavargiya_components': pvb['components'],
|
||||
'external_oracle_comparison': pvb.get('external_oracle_comparison', {'status': 'unavailable'}),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -351,6 +352,7 @@ def calc_panchadhikari_year_lord(
|
||||
'muntha_sign': SIGNS[muntha_sign_idx],
|
||||
'is_daytime': is_daytime,
|
||||
'panchavargiya_engine': winner['panchavargiya_engine'],
|
||||
'panchavargiya_selection_policy': 'native_components_external_observation_only',
|
||||
'candidates': candidates,
|
||||
}
|
||||
|
||||
@@ -1627,8 +1629,13 @@ def _resolve_year_lord_panchavargiya_score(
|
||||
tz=tz,
|
||||
ayanamsa_name=ayanamsa_name,
|
||||
)
|
||||
if external is not None:
|
||||
return external
|
||||
# Upstream 0266b6ad: the annual strength table uses the same native profile;
|
||||
# external availability must not silently change the year-lord selection
|
||||
# method. PyJHora (research switch only) is an observation beside it.
|
||||
comparison = (
|
||||
{**external, 'comparison_role': 'observation_only_not_selection_input'}
|
||||
if external is not None else {'status': 'unavailable'}
|
||||
)
|
||||
if native_scores is None:
|
||||
# Upstream c7117f92 behaviour (dignity proxy); no product caller.
|
||||
proxy = _calc_panchavargiya_bala_for_planet(planet, lon_value)
|
||||
@@ -1636,6 +1643,7 @@ def _resolve_year_lord_panchavargiya_score(
|
||||
'score': proxy['score'],
|
||||
'engine': 'native_proxy',
|
||||
'components': proxy['components'],
|
||||
'external_oracle_comparison': comparison,
|
||||
}
|
||||
# BUG-1217: Tajika five-fold Panchavargiya; None when it cannot be computed.
|
||||
native = native_scores.get(planet) or {}
|
||||
@@ -1646,6 +1654,7 @@ def _resolve_year_lord_panchavargiya_score(
|
||||
'engine': 'tajika_native_five_fold',
|
||||
'raw_total': native['raw_total'],
|
||||
'components': native['components'],
|
||||
'external_oracle_comparison': comparison,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,10 @@ Q5) the candidates are compared by the Tajika five-fold Panchavargiya
|
||||
Drekkana, Navamsa; raw out of 80, score = raw / 4 out of 20;
|
||||
`engine: tajika_native_five_fold`); the dignity proxy no longer selects a year
|
||||
lord. Set JYOTISH_YEAR_LORD_PYJHORA_REFERENCE=1 to pass the annual JD and let
|
||||
the upstream PyJHora lookup run (research comparison only).
|
||||
the upstream PyJHora lookup run (research comparison only). Since upstream
|
||||
0266b6ad (2026-11-07 sync) that lookup never selects: it is attached to each
|
||||
candidate as `external_oracle_comparison` (`comparison_role:
|
||||
observation_only_not_selection_input`) and the native five-fold score decides.
|
||||
|
||||
No stand-in year lord (BUG-1223, product owner 2026-10-04, aligned with
|
||||
upstream 03bea6ed `docs/research/tajika_shared_entrypoint_repair_2026_10_03.md`
|
||||
@@ -140,6 +143,10 @@ def _candidate_list(candidates: List[Dict[str, Any]], winner: Optional[str]) ->
|
||||
"panchavargiya_score": row.get("panchavargiya_score"),
|
||||
"panchavargiya_raw_total": row.get("panchavargiya_raw_total"),
|
||||
"panchavargiya_engine": row.get("panchavargiya_engine"),
|
||||
# Upstream 0266b6ad: the native components that produced the score;
|
||||
# an external engine is an observation beside it, never the selector.
|
||||
"panchavargiya_components": row.get("panchavargiya_components"),
|
||||
"external_oracle_comparison": row.get("external_oracle_comparison"),
|
||||
"selected": row.get("planet") == winner,
|
||||
} for row in candidates if isinstance(row, dict)]
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ CALCULATION_READINESS = {
|
||||
}
|
||||
FAMILY_VALUE = {"dasha": 18, "kp": 17, "pravesha": 16, "sphuta": 12, "chakra": 11, "panchanga": 14}
|
||||
NON_IMPLEMENTATION_SCRIPTS = {
|
||||
"scripts/audit_local_knowledge_copies.py",
|
||||
"scripts/book_rule_lineage.py",
|
||||
"scripts/technique_gap_scanner.py",
|
||||
"scripts/technique_source_adapters.py",
|
||||
|
||||
@@ -788,9 +788,15 @@ class UnifiedConsultationOrchestrator:
|
||||
@staticmethod
|
||||
def _section(value: Any, source_path: str) -> dict[str, Any]:
|
||||
present = bool(value)
|
||||
# Upstream ba8e36ec: a wrapped producer status is not a delivered section.
|
||||
unavailable = isinstance(value, dict) and value.get("status") in {
|
||||
"blocked", "missing", "error", "failed", "not_executed", "not_applicable",
|
||||
"not_available_yet",
|
||||
}
|
||||
return {
|
||||
"status": "used" if present else "missing",
|
||||
"status": "blocked" if unavailable else "used" if present else "missing",
|
||||
"source_path": source_path,
|
||||
"claim_boundary": "payload_presence_not_independent_execution_verification",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -981,6 +987,7 @@ class UnifiedConsultationOrchestrator:
|
||||
# policy can require the second without the first standing in for it.
|
||||
"dasha_sub_periods": self._section(modules.get("dasha_sub_periods"), "modules.dasha_sub_periods"),
|
||||
"narayana_dasha": self._section(modules.get("narayana_dasha"), "modules.narayana_dasha"),
|
||||
"transit": self._section(modules.get("transit_multi_reference"), "modules.transit_multi_reference"),
|
||||
"shadbala": self._section(modules.get("shadbala") or chart_data.get("shadbala"), "modules.shadbala"),
|
||||
"ashtakavarga": self._section(modules.get("ashtakavarga") or chart_data.get("ashtakavarga"), "modules.ashtakavarga"),
|
||||
"yogas": self._section(modules.get("yogas") or chart_data.get("yogas"), "modules.yogas"),
|
||||
@@ -1045,7 +1052,7 @@ class UnifiedConsultationOrchestrator:
|
||||
spectrum if spectrum.get("status") == "used" else None,
|
||||
"modules.varga_spectrum",
|
||||
)
|
||||
missing = [name for name, section in sections.items() if section.get("status") == "missing"]
|
||||
missing = [name for name, section in sections.items() if section.get("status") in {"missing", "blocked"}]
|
||||
signals = chart_data.get("cross_system_signals")
|
||||
if not isinstance(signals, list):
|
||||
signals = modules.get("cross_system_signals") if isinstance(modules.get("cross_system_signals"), list) else []
|
||||
|
||||
@@ -17,9 +17,9 @@ Generated from ontology, family templates and public interface documentation. It
|
||||
- Use for: doctrine pack required before interpretation
|
||||
- Never use as: hand-written period dates, standalone event certainty
|
||||
- Required rule fields: seed, applicability, sequence, duration_model, birth_balance, child_period_model, year_definition
|
||||
- Local evidence hits: references/book_rule_technique_lineage.json, references/technique_registry.json, scripts/bhrigu_pada_dasha.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_pd_source_closure_ledger.py, scripts/chara_narayana_public_dated_examples_round2.py, scripts/chara_narayana_public_dated_examples_round3.py, scripts/chara_narayana_public_dated_examples_round4.py, scripts/chara_narayana_public_dated_examples_round5.py, scripts/chara_narayana_public_dated_examples_round7.py, scripts/chara_narayana_source_field_extraction.py, scripts/chara_narayana_source_rule_ledger.py, scripts/event_prediction_model.py, scripts/full_technique_invocation_matrix.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_next_gap_closure_ledger.py, scripts/jyotish_public_gap_intake_round2.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/marriage_counting.py, scripts/narayana_continuation_source_sweep.py, scripts/pl9_core_table_extractor.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/reading_orchestrator.py, scripts/relocation_signal.py, scripts/specialized_indian_closure_review.py, scripts/sthira_brahma_selector_contract.py, scripts/technique_promotion_audit_varga_karaka.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/vedastro_official_capability_runner.py, scripts/vedastro_parity_matrix.py, scripts/vedastro_python_bridge.py, scripts/vedastro_service_adapter.py
|
||||
- Local implementation-candidate hits: scripts/bhrigu_pada_dasha.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_pd_source_closure_ledger.py, scripts/chara_narayana_public_dated_examples_round2.py, scripts/chara_narayana_public_dated_examples_round3.py, scripts/chara_narayana_public_dated_examples_round4.py, scripts/chara_narayana_public_dated_examples_round5.py, scripts/chara_narayana_public_dated_examples_round7.py, scripts/chara_narayana_source_field_extraction.py, scripts/chara_narayana_source_rule_ledger.py, scripts/event_prediction_model.py, scripts/full_technique_invocation_matrix.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_next_gap_closure_ledger.py, scripts/jyotish_public_gap_intake_round2.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/marriage_counting.py, scripts/narayana_continuation_source_sweep.py, scripts/pl9_core_table_extractor.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/reading_orchestrator.py, scripts/relocation_signal.py, scripts/specialized_indian_closure_review.py, scripts/sthira_brahma_selector_contract.py, scripts/technique_promotion_audit_varga_karaka.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/vedastro_official_capability_runner.py, scripts/vedastro_parity_matrix.py, scripts/vedastro_python_bridge.py, scripts/vedastro_service_adapter.py
|
||||
- Book rule links: boney_major_life_events_composite_parashari_jaimini_timing
|
||||
- Local evidence hits: references/book_rule_technique_lineage.json, references/technique_registry.json, scripts/bhrigu_pada_dasha.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_pd_source_closure_ledger.py, scripts/chara_narayana_public_dated_examples_round2.py, scripts/chara_narayana_public_dated_examples_round3.py, scripts/chara_narayana_public_dated_examples_round4.py, scripts/chara_narayana_public_dated_examples_round5.py, scripts/chara_narayana_public_dated_examples_round7.py, scripts/chara_narayana_source_field_extraction.py, scripts/chara_narayana_source_rule_ledger.py, scripts/event_prediction_model.py, scripts/full_technique_invocation_matrix.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_next_gap_closure_ledger.py, scripts/jyotish_public_gap_intake_round2.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/narayana_continuation_source_sweep.py, scripts/pl9_core_table_extractor.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/reading_orchestrator.py, scripts/relocation_signal.py, scripts/specialized_indian_closure_review.py, scripts/sthira_brahma_selector_contract.py, scripts/technique_promotion_audit_varga_karaka.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/vedastro_official_capability_runner.py, scripts/vedastro_parity_matrix.py, scripts/vedastro_python_bridge.py, scripts/vedastro_service_adapter.py
|
||||
- Local implementation-candidate hits: scripts/bhrigu_pada_dasha.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_pd_source_closure_ledger.py, scripts/chara_narayana_public_dated_examples_round2.py, scripts/chara_narayana_public_dated_examples_round3.py, scripts/chara_narayana_public_dated_examples_round4.py, scripts/chara_narayana_public_dated_examples_round5.py, scripts/chara_narayana_public_dated_examples_round7.py, scripts/chara_narayana_source_field_extraction.py, scripts/chara_narayana_source_rule_ledger.py, scripts/event_prediction_model.py, scripts/full_technique_invocation_matrix.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_next_gap_closure_ledger.py, scripts/jyotish_public_gap_intake_round2.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/narayana_continuation_source_sweep.py, scripts/pl9_core_table_extractor.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/reading_orchestrator.py, scripts/relocation_signal.py, scripts/specialized_indian_closure_review.py, scripts/sthira_brahma_selector_contract.py, scripts/technique_promotion_audit_varga_karaka.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/vedastro_official_capability_runner.py, scripts/vedastro_parity_matrix.py, scripts/vedastro_python_bridge.py, scripts/vedastro_service_adapter.py
|
||||
- Book rule links: boney_major_life_events_composite_parashari_jaimini_timing, jaimini_seventh_from_karakamsha_marriage, jaimini_seventh_from_swamsha_marriage, spouse_meeting_place_via_seventh_from_venus
|
||||
- Book topic links: jaimini_chara_narayana
|
||||
- Documented external module: `jhora.horoscope.dhasa.raasi.chara`
|
||||
- Documented external methods: `get_dhasa_antardhasa(dob,tob,place,divisional_chart_factor=1,years=1,months=1,sixty_hours=1,include_antardasa=True)`
|
||||
@@ -89,8 +89,8 @@ Generated from ontology, family templates and public interface documentation. It
|
||||
- Use for: doctrine pack required before interpretation
|
||||
- Never use as: hand-written period dates, standalone event certainty
|
||||
- Required rule fields: seed, applicability, sequence, duration_model, birth_balance, child_period_model, year_definition
|
||||
- Local evidence hits: references/book_rule_technique_lineage.json, references/technique_registry.json, scripts/active_rectification_candidates.py, scripts/active_rectification_event_engine.py, scripts/active_rectification_questions.py, scripts/buddhi_gati.py, scripts/candidate_multi_engine_observation_replay_2026_07_23.py, scripts/canonical_profile_regression.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_karan_singh_public_replay.py, scripts/chara_narayana_krymsk_d1_row_replay.py, scripts/chara_narayana_maneka_gandhi_md_table_replay.py, scripts/chara_narayana_public_dated_examples_replay.py, scripts/chara_narayana_rath_d16_table40_replay.py, scripts/chara_narayana_rath_vimsamsa_md_ad_replay.py, scripts/chara_narayana_rath_vimsamsa_runtime_probe.py, scripts/chara_narayana_rath_wadiyar_table25_replay.py, scripts/chara_narayana_round6_pd_order_reverse_probe.py, scripts/chara_narayana_round6_runtime_probe.py, scripts/chara_narayana_source_rule_ledger.py, scripts/chara_narayana_us_election_public_candidate_compatibility.py, scripts/chara_narayana_vajpayee_md_table_replay.py, scripts/cmd_narayana_dasha.py, scripts/commercial_astrology_e2e_acceptance_runner.py, scripts/current_blocked_optimization_audit.py, scripts/event_adjudication_receipt.py, scripts/evidence_labeled_reporting.py, scripts/extended_dashas.py, scripts/extract_research_export_sample.py, scripts/full_report_quality_gate.py, scripts/full_spectrum_invocation_plan.py, scripts/full_technique_invocation_matrix.py, scripts/global_top3_jyotish_audit.py, scripts/guided_topic_discovery.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_skill_architecture_registry.py, scripts/karan_singh_narayana_dated_event_stack_replay.py, scripts/karan_singh_narayana_duration_profile_arbitration.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/karan_singh_narayana_real_chart_duration_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/lagnamsaka_dasha.py, scripts/life_stage_hook.py, scripts/local_accepted_evidence_fixtures.py, scripts/narayana_dasha.py, scripts/narayana_direction_closure_packet.py, scripts/narayana_pl9_profile_probe.py, scripts/narayana_variant_regression.py, scripts/padanadhamsa.py, scripts/pl9_full_parity_dashboard.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/profile_aware_benchmark_boundary_dashboard.py, scripts/public_oracle_replay.py, scripts/public_real_case_benchmark.py, scripts/real_case_website_e2e_eval.py, scripts/rectification_report_replay.py, scripts/sanjay_rath_narayana_book_ledger.py, scripts/specialized_dasha_external_replay.py, scripts/specialized_dasha_router.py, scripts/sudarsana_dasha.py, scripts/sudasa.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/trikona_dasha.py, scripts/trikona_raman.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/varnada_dasha.py, scripts/varsha_narayana.py, scripts/vedastro_evidence_orchestrator.py, scripts/vedastro_parity_matrix.py
|
||||
- Local implementation-candidate hits: scripts/active_rectification_candidates.py, scripts/active_rectification_event_engine.py, scripts/active_rectification_questions.py, scripts/buddhi_gati.py, scripts/candidate_multi_engine_observation_replay_2026_07_23.py, scripts/canonical_profile_regression.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_karan_singh_public_replay.py, scripts/chara_narayana_krymsk_d1_row_replay.py, scripts/chara_narayana_maneka_gandhi_md_table_replay.py, scripts/chara_narayana_public_dated_examples_replay.py, scripts/chara_narayana_rath_d16_table40_replay.py, scripts/chara_narayana_rath_vimsamsa_md_ad_replay.py, scripts/chara_narayana_rath_vimsamsa_runtime_probe.py, scripts/chara_narayana_rath_wadiyar_table25_replay.py, scripts/chara_narayana_round6_pd_order_reverse_probe.py, scripts/chara_narayana_round6_runtime_probe.py, scripts/chara_narayana_source_rule_ledger.py, scripts/chara_narayana_us_election_public_candidate_compatibility.py, scripts/chara_narayana_vajpayee_md_table_replay.py, scripts/cmd_narayana_dasha.py, scripts/commercial_astrology_e2e_acceptance_runner.py, scripts/current_blocked_optimization_audit.py, scripts/event_adjudication_receipt.py, scripts/evidence_labeled_reporting.py, scripts/extended_dashas.py, scripts/extract_research_export_sample.py, scripts/full_report_quality_gate.py, scripts/full_spectrum_invocation_plan.py, scripts/full_technique_invocation_matrix.py, scripts/global_top3_jyotish_audit.py, scripts/guided_topic_discovery.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_skill_architecture_registry.py, scripts/karan_singh_narayana_dated_event_stack_replay.py, scripts/karan_singh_narayana_duration_profile_arbitration.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/karan_singh_narayana_real_chart_duration_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/lagnamsaka_dasha.py, scripts/life_stage_hook.py, scripts/local_accepted_evidence_fixtures.py, scripts/narayana_dasha.py, scripts/narayana_direction_closure_packet.py, scripts/narayana_pl9_profile_probe.py, scripts/narayana_variant_regression.py, scripts/padanadhamsa.py, scripts/pl9_full_parity_dashboard.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/profile_aware_benchmark_boundary_dashboard.py, scripts/public_oracle_replay.py, scripts/public_real_case_benchmark.py, scripts/real_case_website_e2e_eval.py, scripts/rectification_report_replay.py, scripts/sanjay_rath_narayana_book_ledger.py, scripts/specialized_dasha_external_replay.py, scripts/specialized_dasha_router.py, scripts/sudarsana_dasha.py, scripts/sudasa.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/trikona_dasha.py, scripts/trikona_raman.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/varnada_dasha.py, scripts/varsha_narayana.py, scripts/vedastro_evidence_orchestrator.py, scripts/vedastro_parity_matrix.py
|
||||
- Local evidence hits: references/book_rule_technique_lineage.json, references/technique_registry.json, scripts/active_rectification_candidates.py, scripts/active_rectification_event_engine.py, scripts/active_rectification_questions.py, scripts/buddhi_gati.py, scripts/candidate_multi_engine_observation_replay_2026_07_23.py, scripts/canonical_profile_regression.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_karan_singh_public_replay.py, scripts/chara_narayana_krymsk_d1_row_replay.py, scripts/chara_narayana_maneka_gandhi_md_table_replay.py, scripts/chara_narayana_public_dated_examples_replay.py, scripts/chara_narayana_rath_d16_table40_replay.py, scripts/chara_narayana_rath_vimsamsa_md_ad_replay.py, scripts/chara_narayana_rath_vimsamsa_runtime_probe.py, scripts/chara_narayana_rath_wadiyar_table25_replay.py, scripts/chara_narayana_round6_pd_order_reverse_probe.py, scripts/chara_narayana_round6_runtime_probe.py, scripts/chara_narayana_source_rule_ledger.py, scripts/chara_narayana_us_election_public_candidate_compatibility.py, scripts/chara_narayana_vajpayee_md_table_replay.py, scripts/cmd_narayana_dasha.py, scripts/commercial_astrology_e2e_acceptance_runner.py, scripts/current_blocked_optimization_audit.py, scripts/event_adjudication_receipt.py, scripts/evidence_labeled_reporting.py, scripts/extended_dashas.py, scripts/extract_research_export_sample.py, scripts/finance_astrology_support_review.py, scripts/full_report_quality_gate.py, scripts/full_spectrum_invocation_plan.py, scripts/full_technique_invocation_matrix.py, scripts/global_top3_jyotish_audit.py, scripts/goel_jaimini_strength.py, scripts/guided_topic_discovery.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_skill_architecture_registry.py, scripts/karan_singh_narayana_dated_event_stack_replay.py, scripts/karan_singh_narayana_duration_profile_arbitration.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/karan_singh_narayana_real_chart_duration_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/lagnamsaka_dasha.py, scripts/life_stage_hook.py, scripts/local_accepted_evidence_fixtures.py, scripts/narayana_dasha.py, scripts/narayana_direction_closure_packet.py, scripts/narayana_pl9_profile_probe.py, scripts/narayana_variant_regression.py, scripts/padanadhamsa.py, scripts/pl9_full_parity_dashboard.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/profile_aware_benchmark_boundary_dashboard.py, scripts/public_oracle_replay.py, scripts/public_real_case_benchmark.py, scripts/real_case_website_e2e_eval.py, scripts/rectification_report_replay.py, scripts/sanjay_rath_narayana_book_ledger.py, scripts/specialized_dasha_external_replay.py, scripts/specialized_dasha_router.py, scripts/sudarsana_dasha.py, scripts/sudasa.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/trikona_dasha.py, scripts/trikona_raman.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/varnada_dasha.py, scripts/varsha_narayana.py, scripts/vedastro_evidence_orchestrator.py, scripts/vedastro_parity_matrix.py
|
||||
- Local implementation-candidate hits: scripts/active_rectification_candidates.py, scripts/active_rectification_event_engine.py, scripts/active_rectification_questions.py, scripts/buddhi_gati.py, scripts/candidate_multi_engine_observation_replay_2026_07_23.py, scripts/canonical_profile_regression.py, scripts/chara_narayana_book_golden_cases.py, scripts/chara_narayana_book_runtime_replay.py, scripts/chara_narayana_karan_singh_public_replay.py, scripts/chara_narayana_krymsk_d1_row_replay.py, scripts/chara_narayana_maneka_gandhi_md_table_replay.py, scripts/chara_narayana_public_dated_examples_replay.py, scripts/chara_narayana_rath_d16_table40_replay.py, scripts/chara_narayana_rath_vimsamsa_md_ad_replay.py, scripts/chara_narayana_rath_vimsamsa_runtime_probe.py, scripts/chara_narayana_rath_wadiyar_table25_replay.py, scripts/chara_narayana_round6_pd_order_reverse_probe.py, scripts/chara_narayana_round6_runtime_probe.py, scripts/chara_narayana_source_rule_ledger.py, scripts/chara_narayana_us_election_public_candidate_compatibility.py, scripts/chara_narayana_vajpayee_md_table_replay.py, scripts/cmd_narayana_dasha.py, scripts/commercial_astrology_e2e_acceptance_runner.py, scripts/current_blocked_optimization_audit.py, scripts/event_adjudication_receipt.py, scripts/evidence_labeled_reporting.py, scripts/extended_dashas.py, scripts/extract_research_export_sample.py, scripts/finance_astrology_support_review.py, scripts/full_report_quality_gate.py, scripts/full_spectrum_invocation_plan.py, scripts/full_technique_invocation_matrix.py, scripts/global_top3_jyotish_audit.py, scripts/goel_jaimini_strength.py, scripts/guided_topic_discovery.py, scripts/jaimini.py, scripts/jyotish_api_server.py, scripts/jyotish_engine.py, scripts/jyotish_skill_architecture_registry.py, scripts/karan_singh_narayana_dated_event_stack_replay.py, scripts/karan_singh_narayana_duration_profile_arbitration.py, scripts/karan_singh_narayana_event_pd_replay.py, scripts/karan_singh_narayana_real_chart_duration_replay.py, scripts/kn_rao_bhrigu_aggregation_ledger.py, scripts/lagnamsaka_dasha.py, scripts/life_stage_hook.py, scripts/local_accepted_evidence_fixtures.py, scripts/narayana_dasha.py, scripts/narayana_direction_closure_packet.py, scripts/narayana_pl9_profile_probe.py, scripts/narayana_variant_regression.py, scripts/padanadhamsa.py, scripts/pl9_full_parity_dashboard.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/profile_aware_benchmark_boundary_dashboard.py, scripts/public_oracle_replay.py, scripts/public_real_case_benchmark.py, scripts/real_case_website_e2e_eval.py, scripts/rectification_report_replay.py, scripts/sanjay_rath_narayana_book_ledger.py, scripts/specialized_dasha_external_replay.py, scripts/specialized_dasha_router.py, scripts/sudarsana_dasha.py, scripts/sudasa.py, scripts/technique_registry.py, scripts/timing_evidence_packets.py, scripts/trikona_dasha.py, scripts/trikona_raman.py, scripts/unified_calculation_archive.py, scripts/unified_consultation_orchestrator.py, scripts/varnada_dasha.py, scripts/varsha_narayana.py, scripts/vedastro_evidence_orchestrator.py, scripts/vedastro_parity_matrix.py
|
||||
- Book rule links: web_pvr_varsha_narayana_ch30_4
|
||||
- Book topic links: jaimini_chara_narayana
|
||||
- Documented external module: `jhora.horoscope.dhasa.raasi.narayana`
|
||||
@@ -387,12 +387,12 @@ Generated from ontology, family templates and public interface documentation. It
|
||||
- Canonical ID: `niryaana_shoola_dasha`
|
||||
- Legacy family priority (not scoped development count): P0 / 88
|
||||
- Next work: complete_variant_and_report_contract
|
||||
- Remaining requirements: See missing dimensions and source-specific usage notes
|
||||
- Remaining requirements: source-backed AD/PD rule and row-level replay; independent same-profile MD case; no predictive or global admission
|
||||
- Native producer: `scripts.niryaana_shoola_dasha.calculate_niryaana_shoola_dasha`
|
||||
- Producer maturity: `source_bounded_wired`
|
||||
- Producer verification scope: stronger Lagna/seventh selection, explicit tie-break, named Goel/Raman/PyJHora duration profiles and MD/AD/PD boundaries
|
||||
- Producer verification scope: PVR second/eighth ayur-strength selection with unresolved ties blocked; movable/fixed/dual 7/8/9 calendar-year MD; external observation separate
|
||||
- Report paths: `jyotish_engine.worksheets.timing_and_predictive_systems.dasha_master_pack.families.niryaana_shoola`, `references/oracle/pl9_p92_p103_field_audit_2026_09_11.json`
|
||||
- Variant profiles: `niryaana_shoola_goel_profile` (adopted_source_bounded), `niryaana_shoola_raman_profile` (optional_source_profile), `niryaana_shoola_pyjhora_observation_profile` (external_observation_only)
|
||||
- Variant profiles: `pvr_niryaana_shoola_v1` (adopted_source_bounded), `niryaana_shoola_pyjhora_observation_profile` (external_observation_only)
|
||||
- Required inputs: birth_datetime, birth_location, ayanamsa, rule_profile
|
||||
- Use for: doctrine pack required before interpretation
|
||||
- Never use as: hand-written period dates, standalone event certainty
|
||||
@@ -445,8 +445,8 @@ Generated from ontology, family templates and public interface documentation. It
|
||||
- Use for: doctrine pack required before interpretation
|
||||
- Never use as: hand-written period dates, standalone event certainty
|
||||
- Required rule fields: seed, applicability, sequence, duration_model, birth_balance, child_period_model, year_definition
|
||||
- Local evidence hits: scripts/extended_dashas.py, scripts/jyotish_engine.py, scripts/niryaana_shoola_dasha.py, scripts/niryaana_shoola_family_contract.py, scripts/pl9_named_dasha_row_replay.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/shoola_dasha.py, scripts/shoola_family_contract.py
|
||||
- Local implementation-candidate hits: scripts/extended_dashas.py, scripts/jyotish_engine.py, scripts/niryaana_shoola_dasha.py, scripts/niryaana_shoola_family_contract.py, scripts/pl9_named_dasha_row_replay.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/shoola_dasha.py, scripts/shoola_family_contract.py
|
||||
- Local evidence hits: scripts/dasha_profile_arbitration.py, scripts/extended_dashas.py, scripts/jyotish_engine.py, scripts/niryaana_shoola_dasha.py, scripts/niryaana_shoola_family_contract.py, scripts/pl9_named_dasha_row_replay.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/shoola_dasha.py, scripts/shoola_family_contract.py
|
||||
- Local implementation-candidate hits: scripts/dasha_profile_arbitration.py, scripts/extended_dashas.py, scripts/jyotish_engine.py, scripts/niryaana_shoola_dasha.py, scripts/niryaana_shoola_family_contract.py, scripts/pl9_named_dasha_row_replay.py, scripts/pl9_p92_p103_field_audit.py, scripts/pl9_report_corpus_audit.py, scripts/professional_parity_closure.py, scripts/shoola_dasha.py, scripts/shoola_family_contract.py
|
||||
- Book rule links: not mapped
|
||||
- Book topic links: not mapped
|
||||
- Documented external module: `jhora.horoscope.dhasa.raasi.shoola`
|
||||
|
||||
@@ -107,6 +107,8 @@ Sudarsana Dasha 已有两个独立的原生太阳弧 MD/AD/PD profile,见
|
||||
[原生与外部用法包](references/sudarsana-dasha-usage.md)。教材从父星座起子期,2004研究从父宫主本命落座起;必须显式选择,不混合。外部固定年观察和旧占位拒绝保持独立。静态 Sudarshana 三参考盘已可调用
|
||||
[严格结构用法包](references/sudarshana-structure-usage.md),不要混同二者。
|
||||
Chandra Kala Nadi 已有[非均匀具名分段及误差候选入口](references/chandra-kala-nadi-usage.md):Patel十六分盘边界并集,保留均分对照profile、三类星座编号和几何子段;不是D150落座或命运判断。
|
||||
|
||||
Meena Jeeva/Sareera 结构入口:`jyotish_engine.py nadi-stellar-roles --request <local.json>`,复用 `nakshatra_advanced.calculate_stellar_jeeva_sareera`。采用具名的原书 printed-p89 步骤;缺星宿方向力或交点尊贵口径时保留 blocked,不得代入 Shadbala。详见 `docs/research/stellar_jeeva_sareera_2026_11_07.md`;尚不启用预测评分或客户导出。
|
||||
这些条目可以用于方法比较或缺口研究,但必须保留原始状态,不得写入确定性
|
||||
应期或客户报告。
|
||||
|
||||
|
||||
@@ -557,8 +557,8 @@ Static entrypoint presence is not a runtime claim: not_run_by_scanner. Original
|
||||
- Use for: named nonuniform Nadi amsa lookup, geometric subparts and uncertainty candidates
|
||||
- Never use as: generic equal D150 substitution, standalone predictive or rectification verdict
|
||||
- Required rule fields: boundary union, sign modality numbering, named source list, half-open endpoints, uncertainty and geometric subparts
|
||||
- Local evidence hits: references/book_rule_packets.json, references/book_rule_technique_lineage.json, references/internet_technique_gap_registry.json, scripts/chandra_kala_nadi.py, scripts/nadi_book_extraction_workflow.py, scripts/nadi_knowledge_extraction_plan.py
|
||||
- Local implementation-candidate hits: scripts/chandra_kala_nadi.py, scripts/nadi_book_extraction_workflow.py, scripts/nadi_knowledge_extraction_plan.py
|
||||
- Local evidence hits: references/book_rule_packets.json, references/book_rule_technique_lineage.json, references/internet_technique_gap_registry.json, scripts/audit_local_knowledge_copies.py, scripts/chandra_kala_nadi.py, scripts/nadi_book_extraction_workflow.py, scripts/nadi_knowledge_extraction_plan.py, scripts/nadi_leaf_assistant.py
|
||||
- Local implementation-candidate hits: scripts/chandra_kala_nadi.py, scripts/nadi_book_extraction_workflow.py, scripts/nadi_knowledge_extraction_plan.py, scripts/nadi_leaf_assistant.py
|
||||
- Book rule links: patel_nadi_ch1_ch2_named_nonuniform
|
||||
- Book topic links: nadi
|
||||
- Documented external module: `not documented`
|
||||
@@ -1073,7 +1073,7 @@ Static entrypoint presence is not a runtime claim: not_run_by_scanner. Original
|
||||
- Use for: source-labelled sensitive-point sums and classical_local D9 parity
|
||||
- Never use as: fertility diagnosis, medical probability, event timing or outcome verdict
|
||||
- Required rule fields: formula, point_names, normalization, gulika_or_mandi_convention, interpretation_scope
|
||||
- Local evidence hits: scripts/beeja_kshetra_sphuta.py
|
||||
- Local evidence hits: scripts/audit_local_knowledge_copies.py, scripts/beeja_kshetra_sphuta.py
|
||||
- Local implementation-candidate hits: scripts/beeja_kshetra_sphuta.py
|
||||
- Book rule links: not mapped
|
||||
- Book topic links: not mapped
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Ported from upstream 8813c851 (astrologer branch d09feab7, 2026-11-07 sync):
|
||||
BCP is observed at an explicit target date; the birth epoch is only a labelled default."""
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_bcp_report_uses_explicit_target_epoch_and_labels_birth_default():
|
||||
from scripts.bhrigu_pada_dasha import bhrigu_pada_dasha_full_report
|
||||
natal = bhrigu_pada_dasha_full_report(10., 2451545.)
|
||||
later = bhrigu_pada_dasha_full_report(10., 2451545., target_date_jd=2451910.25)
|
||||
assert natal['bcp_cycle']['years_elapsed'] == 0
|
||||
assert natal['bcp_reference']['scope'] == 'birth_epoch_default'
|
||||
assert later['bcp_cycle']['years_elapsed'] == 1
|
||||
assert later['bcp_cycle']['bcp_longitude'] == 40.
|
||||
assert later['bcp_reference'] == {'target_jd': 2451910.25, 'scope': 'explicit_target'}
|
||||
assert later['dasha_sequence'] == natal['dasha_sequence']
|
||||
|
||||
|
||||
def test_full_reading_passes_the_reading_date_to_bcp(monkeypatch):
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from datetime import datetime
|
||||
import swisseph as swe
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
|
||||
import bhrigu_pada_dasha
|
||||
calls = []
|
||||
|
||||
def calculate(moon, jd, **kwargs):
|
||||
calls.append((moon, jd, kwargs.get('target_date_jd')))
|
||||
return {'dasha_sequence': []}
|
||||
monkeypatch.setattr(bhrigu_pada_dasha, 'bhrigu_pada_dasha_full_report', calculate)
|
||||
tree = ast.parse((Path(__file__).resolve().parents[1] / 'scripts/jyotish_engine.py').read_text())
|
||||
blocks = [n for n in ast.walk(tree) if isinstance(n, ast.Try) and any(
|
||||
isinstance(item, ast.ImportFrom) and item.module == 'bhrigu_pada_dasha' for item in n.body)]
|
||||
assert len(blocks) == 1
|
||||
env = {'report': {'modules': {}, 'errors': []}, 'planet_lons': {'Moon': 10.}, 'jd': 2451545.,
|
||||
'_varga_planet_lons': lambda value: {}, 'SIGNS': [], 'SIGN_LORDS': {},
|
||||
'args': SimpleNamespace(transit_date='2001-01-01', today='2000-01-01', tz=0),
|
||||
'swe': swe, 'datetime': datetime}
|
||||
exec(compile(ast.Module(body=blocks, type_ignores=[]), '<bhrigu-full-reading>', 'exec'), env)
|
||||
assert calls == [(10., 2451545., 2451911.)]
|
||||
assert not env['report']['errors']
|
||||
module = env['report']['modules']['bhrigu_pada_dasha']
|
||||
assert module['status'] == 'parameter_sensitive'
|
||||
assert module['bcp_reference_date'] == '2001-01-01'
|
||||
@@ -0,0 +1,24 @@
|
||||
import pytest
|
||||
|
||||
from scripts.jyotish_engine import calc_dispositor_chain
|
||||
|
||||
|
||||
def test_dispositor_location_is_not_the_owned_sign():
|
||||
chain = calc_dispositor_chain("Mercury", {
|
||||
"Mercury": {"sign": "Leo"}, "Sun": {"sign": "Cancer"},
|
||||
"Moon": {"sign": "Cancer"},
|
||||
})
|
||||
assert [(row["planet"], row["dispositor"], row["dispositor_sign"]) for row in chain] == [
|
||||
("Mercury", "Sun", "Cancer"), ("Sun", "Moon", "Cancer"), ("Moon", "Moon", "Cancer")]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lord", [None, {}, "unavailable"])
|
||||
def test_missing_dispositor_location_is_not_invented(lord):
|
||||
chain = calc_dispositor_chain("Mercury", {"Mercury": {"sign": "Leo"}, "Sun": lord})
|
||||
assert len(chain) == 1
|
||||
assert chain[0]["dispositor_sign"] is None
|
||||
|
||||
|
||||
def test_exchange_keeps_both_actual_locations_without_infinite_loop():
|
||||
chain = calc_dispositor_chain("Mars", {"Mars": {"sign": "Taurus"}, "Venus": {"sign": "Aries"}})
|
||||
assert [r["dispositor_sign"] for r in chain] == ["Aries", "Taurus"]
|
||||
@@ -0,0 +1,78 @@
|
||||
from copy import deepcopy
|
||||
import pytest
|
||||
|
||||
from scripts.finance_astrology_support_review import (
|
||||
build_finance_astrology_support_review, build_finance_delivery_inventory,
|
||||
)
|
||||
from scripts.full_report_quality_gate import evaluate_evidence_claims, evaluate_full_report
|
||||
from scripts.unified_consultation_orchestrator import UnifiedConsultationOrchestrator
|
||||
|
||||
|
||||
@pytest.mark.parametrize("status", ["blocked", "missing", "error", "failed",
|
||||
"not_executed", "not_applicable", "not_available_yet"])
|
||||
def test_unavailable_narayana_cannot_become_available_by_wrapping(status):
|
||||
packet = UnifiedConsultationOrchestrator().machine_evidence_packet(
|
||||
chart={"modules": {"narayana_dasha": {"status": status, "reason": "fixture"}}},
|
||||
route_packet={"question_type": "finance"})
|
||||
review = build_finance_astrology_support_review(
|
||||
route_packet={"question_type": "finance"}, machine_evidence_packet=packet,
|
||||
runtime_evidence_log={})
|
||||
assert review["coverage"]["narayana"] == "blocked"
|
||||
assert "narayana_dasha" in packet["missing_sections"]
|
||||
|
||||
|
||||
|
||||
def test_actual_native_sections_reach_finance_review():
|
||||
chart = {"modules": {
|
||||
"varga_full": {"D11_Rudramsa": {"fixture": True}, "D4_Turyamsa": {"fixture": True}},
|
||||
"narayana_dasha": {"periods": [{"sign": "Leo"}]},
|
||||
"transit_multi_reference": {"status": "blocked"},
|
||||
}}
|
||||
packet = UnifiedConsultationOrchestrator().machine_evidence_packet(
|
||||
chart=chart, route_packet={"question_type": "finance"})
|
||||
assert packet["sections"]["D11"]["status"] == "used"
|
||||
assert packet["sections"]["D4"]["status"] == "used"
|
||||
review = build_finance_astrology_support_review(
|
||||
route_packet={"question_type": "finance"}, machine_evidence_packet=packet,
|
||||
runtime_evidence_log={})
|
||||
assert review["coverage"]["narayana"] == "present"
|
||||
assert review["coverage"]["transit"] == "blocked"
|
||||
assert review["readiness"] == "incomplete"
|
||||
|
||||
|
||||
def test_arbitration_is_not_a_narayana_execution_receipt():
|
||||
review = build_finance_astrology_support_review(
|
||||
route_packet={"question_type": "finance"}, machine_evidence_packet={},
|
||||
runtime_evidence_log={"cross_system_arbitration": {"status": "used"}})
|
||||
assert review["coverage"]["narayana"] == "missing"
|
||||
assert review["audit_rows"][0]["effect_on_confidence"] == "incomplete"
|
||||
|
||||
|
||||
def test_delivery_preserves_raw_without_promoting_or_mutating():
|
||||
modules = {"argala": {"status": "blocked", "candidate": [1]},
|
||||
"dispositor_chains": {"chain": ["Mercury", "Jupiter"]}}
|
||||
original = deepcopy(modules)
|
||||
inventory = build_finance_delivery_inventory(modules)
|
||||
rows = {r["technique"]: r for r in inventory["rows"]}
|
||||
assert rows["argala"]["delivery_status"] == "producer_status_blocked"
|
||||
assert rows["sudasa"]["delivery_status"] == "not_delivered"
|
||||
assert rows["dispositor_chains"]["execution_status"] == "not_verified_by_inventory"
|
||||
rows["argala"]["raw"]["candidate"].append(2)
|
||||
assert modules == original
|
||||
|
||||
|
||||
def test_conflicting_claim_binding_blocks_gate():
|
||||
packet = {"points": {"A11": "Leo"}, "evidence_claims": [
|
||||
{"path": ["points", "A11"], "value": "Taurus"}]}
|
||||
assert evaluate_evidence_claims(packet)["errors"] == ["claim_0:value_mismatch"]
|
||||
assert "evidence_claim_bindings_invalid" in evaluate_full_report(packet)["blocking_reasons"]
|
||||
packet["evidence_claims"][0]["value"] = "Leo"
|
||||
assert evaluate_evidence_claims(packet)["status"] == "bindings_match"
|
||||
|
||||
|
||||
def test_missing_and_empty_bindings_never_claim_verification():
|
||||
assert evaluate_evidence_claims({"evidence_claims": []})["status"] == "not_supplied"
|
||||
assert evaluate_evidence_claims({})["status"] == "not_supplied"
|
||||
for path, value in [(["absent"], 0), (["evidence_claims"], []), (["zero"], False)]:
|
||||
packet = {"zero": 0, "evidence_claims": [{"path": path, "value": value}]}
|
||||
assert evaluate_evidence_claims(packet)["status"] == "blocked"
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
import jyotish_engine
|
||||
|
||||
from mcp_server import (
|
||||
@@ -518,7 +520,10 @@ def test_finance_sodhita_and_kakshya_friction_reduce_score_without_forcing_label
|
||||
|
||||
assert base["dominant_label"] == "public_wealth_status"
|
||||
assert weighted["dominant_label"] == "public_wealth_status"
|
||||
assert weighted["score"] == base["score"] - 4
|
||||
# 原值: base - 4(Sodhita -2 + Kakshya -2); 新值: base - 2,且无 sodhita_wealth_friction;
|
||||
# 原因: 产品 2026-11-07 同步占星师工作分支 d09feab7(a5b75886 Sodhita 无已裁定财富阈值,不再投票)。
|
||||
assert weighted["score"] == base["score"] - 2
|
||||
assert "sodhita_wealth_friction" not in weighted["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_derives_wealth_promise_from_dhana_yogas() -> None:
|
||||
@@ -763,7 +768,7 @@ def test_collect_strict_evidence_finance_adds_pav_and_kakshya_support_as_seconda
|
||||
assert "kakshya_finance_support" in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_flags_sodhita_wealth_friction() -> None:
|
||||
def test_collect_strict_evidence_retains_unproven_sodhita_without_friction() -> None:
|
||||
result = {
|
||||
"modules": {
|
||||
"chart": {"ascendant": {"sign": "Aries"}},
|
||||
@@ -814,14 +819,20 @@ def test_collect_strict_evidence_finance_flags_sodhita_wealth_friction() -> None
|
||||
|
||||
strict = _collect_strict_evidence("finance", result)
|
||||
|
||||
# 原值: level obstructive + signals [sodhita_wealth_friction],且进入 secondary_context;
|
||||
# 新值: level none、signals []、status method_unverified、prediction_scoring_allowed False;
|
||||
# 原因: 产品 2026-11-07 同步占星师工作分支 d09feab7(a5b75886)。
|
||||
assert strict["present_evidence"]["sodhita_finance_support"] == {
|
||||
"level": "obstructive",
|
||||
"level": "none",
|
||||
"source": "ashtakavarga_sodhita_bridge_v1",
|
||||
"signals": ["sodhita_wealth_friction"],
|
||||
"signals": [],
|
||||
"target_houses": [2, 11],
|
||||
"raw_scores": {"2": 18, "11": 17},
|
||||
"status": "method_unverified",
|
||||
"prediction_scoring_allowed": False,
|
||||
"reason": "unadjudicated_contribution_subtraction_not_wealth_evidence",
|
||||
}
|
||||
assert "sodhita_wealth_friction" in strict["event_judgement"]["secondary_context"]
|
||||
assert "sodhita_wealth_friction" not in strict["event_judgement"]["secondary_context"]
|
||||
|
||||
|
||||
def test_collect_strict_evidence_finance_flags_kakshya_friction_as_secondary_only() -> None:
|
||||
@@ -1494,3 +1505,79 @@ def test_finance_narrative_payload_forces_monthly_adjudication_layers_into_final
|
||||
assert any("月度主状态" in item for item in payload["strengths"])
|
||||
assert any("阻力来源" in item for item in payload["risks"])
|
||||
assert "时间置信度" in payload["markdown"]
|
||||
|
||||
|
||||
# Ported from upstream 0266b6ad / 8c4bff8a / a5b75886 (d09feab7 branch).
|
||||
@pytest.mark.parametrize('metadata', [
|
||||
{},
|
||||
{'prediction_scoring_allowed': True},
|
||||
{'method_id': 'unspecified_historical_method'},
|
||||
{'method_id': 'unregistered_method', 'prediction_scoring_allowed': True},
|
||||
{'prediction_scoring_allowed': False},
|
||||
{'method': 'Sodhita Ashtakavarga (BPHS标准)'},
|
||||
{'status': 'executed', 'method': 'Trikona Shodhana -> Ekadhipatya Shodhana'},
|
||||
])
|
||||
def test_legacy_sodhita_retains_raw_values_without_wealth_penalty(metadata):
|
||||
from mcp_server import _derive_sodhita_finance_support
|
||||
result = _derive_sodhita_finance_support({'sodhita': {
|
||||
**metadata, 'sodhita_sav': {'assessment': [
|
||||
{'sign': 'Taurus', 'score': 5}, {'sign': 'Aquarius', 'score': 6}]}}}, 'Aries')
|
||||
assert result['raw_scores'] == {'2': 5, '11': 6}
|
||||
assert result['level'] == 'none'
|
||||
assert result['signals'] == []
|
||||
assert result['prediction_scoring_allowed'] is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize('metadata', [
|
||||
{},
|
||||
{'prediction_scoring_allowed': True},
|
||||
{'method_id': 'unspecified_historical_method'},
|
||||
{'method_id': 'unregistered_method', 'prediction_scoring_allowed': True},
|
||||
{'prediction_scoring_allowed': False},
|
||||
{'status': 'legacy_reference_only'},
|
||||
{'status': 'reference_only'},
|
||||
{'status': 'blocked'},
|
||||
{'status': 'incomplete'},
|
||||
{'status': 'not_available_yet'},
|
||||
{'status': 'unavailable'},
|
||||
{'method_id': 'legacy_malefic_contribution_subtraction_v1'},
|
||||
{'method': 'Sodhita Ashtakavarga (BPHS标准)'},
|
||||
])
|
||||
def test_cached_sodhita_restrictions_survive_finance_adjudication(metadata):
|
||||
from copy import deepcopy
|
||||
present = {'vimshottari_current': {'mahadasha': 'Venus'}}
|
||||
baseline = _derive_event_judgement('finance', present, [])
|
||||
present['sodhita_finance_support'] = {
|
||||
**metadata, 'level': 'obstructive',
|
||||
'signals': ['sodhita_wealth_friction'], 'raw_scores': {'2': 5, '11': 6},
|
||||
}
|
||||
original = deepcopy(present)
|
||||
assert _derive_event_judgement('finance', present, []) == baseline
|
||||
assert present == original
|
||||
|
||||
|
||||
@pytest.mark.parametrize('status', ['blocked', 'reference_only', 'unavailable'])
|
||||
def test_sodhita_producer_restriction_survives_bridge(status):
|
||||
from mcp_server import _derive_sodhita_finance_support
|
||||
result = _derive_sodhita_finance_support({'sodhita': {
|
||||
'status': status, 'sodhita_sav': {'assessment': [
|
||||
{'sign': 'Taurus', 'score': 5}, {'sign': 'Aquarius', 'score': 6}]}}}, 'Aries')
|
||||
assert result['raw_scores'] == {'2': 5, '11': 6}
|
||||
assert result['status'] == status
|
||||
assert result['prediction_scoring_allowed'] is False
|
||||
assert result['level'] == 'none'
|
||||
assert result['signals'] == []
|
||||
|
||||
|
||||
# Ported from upstream ba8e36ec.
|
||||
def test_optional_delivery_inventory_does_not_vote_or_fill_required_evidence():
|
||||
baseline = _collect_strict_evidence("finance", {"modules": {}})
|
||||
result = _collect_strict_evidence("finance", {"modules": {
|
||||
"argala": {"status": "blocked", "candidate": "retained"},
|
||||
"sudasa": {"periods": [{"sign": "Leo"}]},
|
||||
}})
|
||||
rows = {r["technique"]: r for r in result["finance_delivery_inventory"]["rows"]}
|
||||
assert rows["argala"]["raw"]["candidate"] == "retained"
|
||||
assert rows["sudasa"]["delivery_status"] == "payload_delivered"
|
||||
assert result["event_judgement"] == baseline["event_judgement"]
|
||||
assert result["missing_evidence"] == baseline["missing_evidence"]
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
"""Synthetic structural cases; no personal data or predictive validation."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.nakshatra_advanced import calculate_stellar_jeeva_sareera
|
||||
|
||||
|
||||
BASE = dict(Sun=125., Moon=45., Mars=215., Mercury=75.,
|
||||
Jupiter=255., Venus=185., Saturn=305., Rahu=10., Ketu=190.)
|
||||
|
||||
|
||||
def run(lord, positions=None, **kwargs):
|
||||
return calculate_stellar_jeeva_sareera(
|
||||
positions or BASE, bhava_lord=lord, ayanamsa="synthetic",
|
||||
node_mode="mean", **kwargs)
|
||||
|
||||
|
||||
def test_own_star_uses_sign_lord_instead_of_self_loop():
|
||||
result = run("Moon")
|
||||
assert (result["jeeva"], result["sareera"]) == ("Moon", "Venus")
|
||||
assert result["prediction_scoring_allowed"] is False
|
||||
|
||||
|
||||
def test_own_star_and_own_sign_retains_both():
|
||||
result = run("Sun", dict(BASE, Sun=148.))
|
||||
assert (result["jeeva"], result["sareera"]) == ("Sun", "Sun")
|
||||
|
||||
|
||||
def test_owner_beats_exalted_companion():
|
||||
result = run("Mercury", dict(BASE, Mercury=20., Saturn=200.))
|
||||
assert result["jeeva"] == "Venus"
|
||||
|
||||
|
||||
def test_original_saturn_anuradha_retains_against_owner():
|
||||
result = run("Saturn", dict(BASE, Saturn=220.))
|
||||
assert result["jeeva"] == "Saturn"
|
||||
|
||||
|
||||
def test_missing_stellar_digbala_is_not_false():
|
||||
result = run("Moon", dict(BASE, Moon=275., Sun=65., Mercury=100., Jupiter=70.))
|
||||
assert result["status"] == "blocked"
|
||||
assert result["jeeva"] is None
|
||||
assert "stellar_digbala" in result["trace"][0]["reason"]
|
||||
|
||||
|
||||
def test_explicit_stellar_digbala_transfers_then_recomputes_sareera():
|
||||
result = run("Moon", dict(BASE, Moon=275., Sun=65., Mercury=100., Jupiter=70.),
|
||||
stellar_digbala={"Sun": False, "Jupiter": True})
|
||||
assert result["jeeva"] == "Jupiter"
|
||||
assert result["sareera"] == "Rahu"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", [float("nan"), float("inf"), True, "45"])
|
||||
def test_invalid_longitude_rejected(value):
|
||||
with pytest.raises(ValueError):
|
||||
run("Moon", dict(BASE, Moon=value))
|
||||
|
||||
|
||||
def test_missing_planet_cannot_hide_a_companion():
|
||||
with pytest.raises(ValueError):
|
||||
run("Moon", {k: v for k, v in BASE.items() if k != "Ketu"})
|
||||
|
||||
|
||||
def test_numeric_shadbala_cannot_masquerade_as_stellar_digbala():
|
||||
with pytest.raises(ValueError):
|
||||
run("Moon", stellar_digbala={"Sun": 60})
|
||||
|
||||
|
||||
def test_exalted_companion_replaces_seed_without_strength_scores():
|
||||
result = run("Moon", dict(BASE, Moon=275., Sun=332., Venus=355.))
|
||||
assert result["jeeva"] == "Venus"
|
||||
assert result["trace"][0]["reason"] == "exalted"
|
||||
|
||||
|
||||
def test_unselected_saturn_does_not_override_owner_under_p89_profile():
|
||||
result = run("Venus", dict(BASE, Saturn=220.))
|
||||
assert result["jeeva"] == "Mars"
|
||||
|
||||
|
||||
def test_nodal_exaltation_not_silently_imported_from_another_school():
|
||||
result = run("Moon", dict(BASE, Moon=275., Sun=65., Mercury=100., Rahu=70.))
|
||||
assert result["status"] == "blocked"
|
||||
assert result["trace"][0]["reason"] == "nodal_exaltation_profile_required"
|
||||
|
||||
|
||||
def test_source_table_roles_replay_with_explicit_stellar_assumption():
|
||||
# Anonymous printed pp92-96 structural example. Not independent verification.
|
||||
# Mars uses 29 degrees: OCR minute is uncertain, same star/sign throughout.
|
||||
chart = dict(Sun=157+52/60, Moon=147+28/60, Mars=239., Mercury=144+51/60,
|
||||
Jupiter=250+29/60, Venus=184+48/60, Saturn=200+2/60,
|
||||
Rahu=36+43/60, Ketu=216+43/60)
|
||||
expected = dict(Sun=("Sun", "Mercury"), Moon=("Sun", "Mercury"),
|
||||
Mars=("Mercury", "Venus"), Mercury=("Venus", "Mars"),
|
||||
Jupiter=("Mars", "Mercury"), Venus=("Mars", "Mercury"),
|
||||
Saturn=("Jupiter", "Mars"))
|
||||
for lord, pair in expected.items():
|
||||
result = run(lord, chart, stellar_digbala={"Moon": False, "Mercury": True})
|
||||
assert (result["jeeva"], result["sareera"]) == pair
|
||||
assert result["external_verified"] is False
|
||||
|
||||
|
||||
def test_native_cli_executes_without_export_or_prediction(tmp_path):
|
||||
request = tmp_path / "synthetic.json"
|
||||
request.write_text(json.dumps(dict(positions=BASE, bhava_lord="Moon",
|
||||
ayanamsa="synthetic", node_mode="mean")))
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/jyotish_engine.py", "nadi-stellar-roles", "--request", str(request)],
|
||||
cwd=root, text=True, capture_output=True, check=True)
|
||||
result = json.loads(completed.stdout)
|
||||
assert result["status"] == "executed"
|
||||
assert result["sareera"] == "Venus"
|
||||
assert result["prediction_scoring_allowed"] is False
|
||||
@@ -0,0 +1,66 @@
|
||||
"""Ported from upstream 0266b6ad (astrologer branch d09feab7, 2026-11-07 sync).
|
||||
|
||||
The year lord is selected by the native Tajika five-fold Panchavargiya; an
|
||||
external (PyJHora) score is an observation beside it and cannot change the
|
||||
selection, the components, or fill missing native inputs. The annual strength
|
||||
table shows the same native score and components.
|
||||
|
||||
Local divergence: upstream's third test relabels the legacy Sun/Mars/Saturn
|
||||
subtraction (`calc_sodhita_av`). That table was removed here (BUG-1209), so the
|
||||
test asserts it stays absent and that the product chain is the classical one.
|
||||
"""
|
||||
from scripts import ashtakavarga, tajika
|
||||
from scripts import tajika_year_lord
|
||||
|
||||
|
||||
POSITIONS = {'Sun': 10., 'Moon': 42., 'Mars': 75., 'Mercury': 108.,
|
||||
'Jupiter': 141., 'Venus': 174., 'Saturn': 207.}
|
||||
|
||||
|
||||
def test_external_score_cannot_change_year_lord_or_native_components(monkeypatch):
|
||||
monkeypatch.setattr(tajika, '_external_pyjhora_panchavargiya_score', lambda *a, **kw: None)
|
||||
baseline = tajika.calc_panchadhikari_year_lord(POSITIONS, 0, 10, 25)
|
||||
monkeypatch.setattr(tajika, '_external_pyjhora_panchavargiya_score',
|
||||
lambda *a, **kw: {'score': 999, 'engine': 'external_fixture', 'components': {}})
|
||||
observed = tajika.calc_panchadhikari_year_lord(POSITIONS, 0, 10, 25)
|
||||
assert observed['year_lord'] == baseline['year_lord']
|
||||
assert observed['panchavargiya_selection_policy'] == 'native_components_external_observation_only'
|
||||
native = tajika._calc_native_panchavargiya_bala(POSITIONS)
|
||||
for row in observed['candidates']:
|
||||
assert row['panchavargiya_score'] == native[row['planet']]['score']
|
||||
assert row['panchavargiya_components'] == native[row['planet']]['components']
|
||||
assert row['panchavargiya_engine'] == 'tajika_native_five_fold'
|
||||
assert row['external_oracle_comparison']['score'] == 999
|
||||
assert row['external_oracle_comparison']['comparison_role'] == 'observation_only_not_selection_input'
|
||||
|
||||
|
||||
def test_external_score_cannot_fill_missing_native_inputs(monkeypatch):
|
||||
monkeypatch.setattr(tajika, '_external_pyjhora_panchavargiya_score',
|
||||
lambda *a, **kw: {'score': 10, 'engine': 'fixture', 'components': {}})
|
||||
assert tajika.calc_panchadhikari_year_lord({'Sun': 10., 'Moon': 42.}, 0, 10, 25)['status'] == 'blocked'
|
||||
|
||||
|
||||
def test_year_lord_selection_and_strength_table_show_the_same_native_score():
|
||||
selected = tajika.calc_panchadhikari_year_lord(POSITIONS, 0, 10, 25)
|
||||
table = tajika.calc_tajika_strength_layers(POSITIONS, 0.0, selected['year_lord'])['panchavargiya_bala']
|
||||
listed = tajika_year_lord._candidate_list(selected['candidates'], selected['year_lord'])
|
||||
assert listed and any(row['selected'] for row in listed)
|
||||
for row in listed:
|
||||
shown = table[row['planet']]
|
||||
assert row['panchavargiya_score'] == shown['score']
|
||||
assert row['panchavargiya_raw_total'] == shown['raw_total']
|
||||
assert row['panchavargiya_components'] == shown['components']
|
||||
assert set(row['panchavargiya_components']) == {
|
||||
'graha_bala', 'uchcha_bala', 'hudda_bala', 'drekkana_bala', 'navamsha_bala'}
|
||||
|
||||
|
||||
def test_legacy_subtraction_table_stays_absent_from_the_product():
|
||||
assert not hasattr(ashtakavarga, 'calc_sodhita_av')
|
||||
planets = {name: {'sign': tajika.SIGNS[int(lon // 30)]} for name, lon in POSITIONS.items()}
|
||||
planets.update({'Rahu': {'sign': 'Aries'}, 'Ketu': {'sign': 'Libra'}})
|
||||
av = ashtakavarga.calc_ashtakavarga(planets, 0)
|
||||
from scripts.ashtakavarga_shodhana import sodhita_chain
|
||||
chain = sodhita_chain(av['bav'], planets)
|
||||
assert chain['status'] == 'executed'
|
||||
assert 'BPHS标准' not in chain['method']
|
||||
assert len(chain['sodhita_sav']['assessment']) == 12
|
||||
Reference in New Issue
Block a user