From c9953c05f23ccd2d716632ed69f20ba63718b3a8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Nov 2026 20:03:44 +0800 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8 --- .../stellar_jeeva_sareera_2026_11_07.md | 100 +++++++++++++++ mcp_server.py | 32 +++-- references/technique_ontology.json | 5 +- scripts/bhrigu_pada_dasha.py | 10 +- scripts/finance_astrology_support_review.py | 58 ++++++++- scripts/full_report_quality_gate.py | 43 +++++++ scripts/jyotish_engine.py | 26 +++- scripts/nakshatra_advanced.py | 109 ++++++++++++++++ scripts/pl9_reader_export.py | 11 +- scripts/tajika.py | 13 +- scripts/tajika_year_lord.py | 9 +- scripts/technique_gap_scanner.py | 1 + scripts/unified_consultation_orchestrator.py | 11 +- .../references/generated-usage-pack.md | 20 +-- skills/specialized-jyotish/SKILL.md | 2 + .../references/generated-usage-pack.md | 6 +- tests/test_bhrigu_bcp_observation_date.py | 45 +++++++ tests/test_dispositor_chain_locations.py | 24 ++++ tests/test_finance_delivery_integrity.py | 78 ++++++++++++ tests/test_mcp_strict_workflow_finance.py | 97 ++++++++++++++- tests/test_stellar_jeeva_sareera.py | 117 ++++++++++++++++++ tests/test_strength_source_integrity.py | 66 ++++++++++ 22 files changed, 837 insertions(+), 46 deletions(-) create mode 100644 docs/research/stellar_jeeva_sareera_2026_11_07.md create mode 100644 tests/test_bhrigu_bcp_observation_date.py create mode 100644 tests/test_dispositor_chain_locations.py create mode 100644 tests/test_finance_delivery_integrity.py create mode 100644 tests/test_stellar_jeeva_sareera.py create mode 100644 tests/test_strength_source_integrity.py diff --git a/docs/research/stellar_jeeva_sareera_2026_11_07.md b/docs/research/stellar_jeeva_sareera_2026_11_07.md new file mode 100644 index 00000000..9bad679d --- /dev/null +++ b/docs/research/stellar_jeeva_sareera_2026_11_07.md @@ -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 `. +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. diff --git a/mcp_server.py b/mcp_server.py index d35f56f6..49cf843d 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -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( diff --git a/references/technique_ontology.json b/references/technique_ontology.json index 4a4adb42..b35468ea 100644 --- a/references/technique_ontology.json +++ b/references/technique_ontology.json @@ -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."}}, diff --git a/scripts/bhrigu_pada_dasha.py b/scripts/bhrigu_pada_dasha.py index 43af663b..2a9e8472 100644 --- a/scripts/bhrigu_pada_dasha.py +++ b/scripts/bhrigu_pada_dasha.py @@ -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 diff --git a/scripts/finance_astrology_support_review.py b/scripts/finance_astrology_support_review.py index 2a2bc3e8..3721c5c9 100644 --- a/scripts/finance_astrology_support_review.py +++ b/scripts/finance_astrology_support_review.py @@ -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 diff --git a/scripts/full_report_quality_gate.py b/scripts/full_report_quality_gate.py index 5bd0acb2..aa091352 100755 --- a/scripts/full_report_quality_gate.py +++ b/scripts/full_report_quality_gate.py @@ -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, diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 94ddc0cc..f478b487 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -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, diff --git a/scripts/nakshatra_advanced.py b/scripts/nakshatra_advanced.py index fe33e3fd..479daa62 100644 --- a/scripts/nakshatra_advanced.py +++ b/scripts/nakshatra_advanced.py @@ -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星宿循环关系 diff --git a/scripts/pl9_reader_export.py b/scripts/pl9_reader_export.py index edfabc9e..a2dd74cd 100644 --- a/scripts/pl9_reader_export.py +++ b/scripts/pl9_reader_export.py @@ -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')) diff --git a/scripts/tajika.py b/scripts/tajika.py index 1a5216cb..d22c7d6b 100644 --- a/scripts/tajika.py +++ b/scripts/tajika.py @@ -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, } diff --git a/scripts/tajika_year_lord.py b/scripts/tajika_year_lord.py index a8baafc8..e4384495 100644 --- a/scripts/tajika_year_lord.py +++ b/scripts/tajika_year_lord.py @@ -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)] diff --git a/scripts/technique_gap_scanner.py b/scripts/technique_gap_scanner.py index b94eec0e..c1455a5d 100644 --- a/scripts/technique_gap_scanner.py +++ b/scripts/technique_gap_scanner.py @@ -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", diff --git a/scripts/unified_consultation_orchestrator.py b/scripts/unified_consultation_orchestrator.py index cb5bfa0e..cbb3005f 100644 --- a/scripts/unified_consultation_orchestrator.py +++ b/scripts/unified_consultation_orchestrator.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 [] diff --git a/skills/jaimini-timing/references/generated-usage-pack.md b/skills/jaimini-timing/references/generated-usage-pack.md index 25aab0df..161176ec 100644 --- a/skills/jaimini-timing/references/generated-usage-pack.md +++ b/skills/jaimini-timing/references/generated-usage-pack.md @@ -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` diff --git a/skills/specialized-jyotish/SKILL.md b/skills/specialized-jyotish/SKILL.md index 9be7cce5..caf7fe5d 100644 --- a/skills/specialized-jyotish/SKILL.md +++ b/skills/specialized-jyotish/SKILL.md @@ -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 `,复用 `nakshatra_advanced.calculate_stellar_jeeva_sareera`。采用具名的原书 printed-p89 步骤;缺星宿方向力或交点尊贵口径时保留 blocked,不得代入 Shadbala。详见 `docs/research/stellar_jeeva_sareera_2026_11_07.md`;尚不启用预测评分或客户导出。 这些条目可以用于方法比较或缺口研究,但必须保留原始状态,不得写入确定性 应期或客户报告。 diff --git a/skills/specialized-jyotish/references/generated-usage-pack.md b/skills/specialized-jyotish/references/generated-usage-pack.md index 0acc4594..75199a9f 100644 --- a/skills/specialized-jyotish/references/generated-usage-pack.md +++ b/skills/specialized-jyotish/references/generated-usage-pack.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 diff --git a/tests/test_bhrigu_bcp_observation_date.py b/tests/test_bhrigu_bcp_observation_date.py new file mode 100644 index 00000000..81c3b9fb --- /dev/null +++ b/tests/test_bhrigu_bcp_observation_date.py @@ -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=[]), '', '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' diff --git a/tests/test_dispositor_chain_locations.py b/tests/test_dispositor_chain_locations.py new file mode 100644 index 00000000..75cc1542 --- /dev/null +++ b/tests/test_dispositor_chain_locations.py @@ -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"] diff --git a/tests/test_finance_delivery_integrity.py b/tests/test_finance_delivery_integrity.py new file mode 100644 index 00000000..86c305f8 --- /dev/null +++ b/tests/test_finance_delivery_integrity.py @@ -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" diff --git a/tests/test_mcp_strict_workflow_finance.py b/tests/test_mcp_strict_workflow_finance.py index af7f0996..7feb8bb1 100644 --- a/tests/test_mcp_strict_workflow_finance.py +++ b/tests/test_mcp_strict_workflow_finance.py @@ -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"] diff --git a/tests/test_stellar_jeeva_sareera.py b/tests/test_stellar_jeeva_sareera.py new file mode 100644 index 00000000..bf008174 --- /dev/null +++ b/tests/test_stellar_jeeva_sareera.py @@ -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 diff --git a/tests/test_strength_source_integrity.py b/tests/test_strength_source_integrity.py new file mode 100644 index 00000000..5f0fc9ce --- /dev/null +++ b/tests/test_strength_source_integrity.py @@ -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