diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index c1cf8120..8bd0643d 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -91,6 +91,7 @@ For large architecture or release work, also read: | ERR-058 | Formula-based Sahams used the day/night operand rules but omitted the documented zodiacal-order `+30°` exception. | mitigated 2026-07-14 | `_calc_formula_saham()` applies the `references/saham_rules.json` forward-arc condition and one-sign correction; keep external numeric oracle parity as a separate `partial` requirement. | | ERR-059 | Gulika was either an approximate fallback or falsely implied as a chart module output. | mitigated 2026-07-14 | `scripts/gulika.py` computes Prasna Marga Ghatika segment Ascendant with Swiss sunrise/sunset and Lahiri sidereal houses. It is exposed only as `prashna_context.supporting_indicators.gulika`, remains `partial`, and cannot unlock Sphuta or verdict layers until external numeric parity exists. | | ERR-060 | Legacy Sphuta functions combined approximate Gulika with interpretive signals, while the exact formula could not be inspected in the production question context. | mitigated 2026-07-14 | `prashna_sphuta.py` exposes formula-only Trisphuta/Catusphuta/Pancasphuta from the partial Gulika evidence. It is supporting-only; Kunda, life-sensitive Sphutas and Prashna verdicts remain blocked pending external numeric parity. | +| ERR-061 | Full-reading passed longitude-only data to the Tajika layer, permanently blocking its speed-dependent seven-planet interaction evidence. | mitigated 2026-07-14 | Pass actual Swiss longitude/speed pairs. The output may expose only partial Ithasala/Easarapha candidates; named chains and event verdicts stay blocked pending golden cases. | ## Fragment Sweep Command Set diff --git a/references/technique_registry.json b/references/technique_registry.json index 182b9ff1..5d0b224f 100644 --- a/references/technique_registry.json +++ b/references/technique_registry.json @@ -1145,7 +1145,7 @@ "audit_label": "Tajika Yogas", "missing_impact": "Tajika year-chart yoga detection incomplete; annual timing confidence capped.", "version": "7.0", - "note": "Production partial: legacy Tajika kernel is under replacement; no event verdict until seven-planet applying/separating and Deeptamsa golden cases pass.", + "note": "Production partial: seven-planet Deeptamsa/applying evidence exposes Ithasala/Easarapha candidates only. Nakta/Yamaya/Manahoo/Kamboola chains and all event verdicts remain blocked pending classic golden cases.", "entry_type": "supporting_indicator", "evidence_role": "secondary", "user_visibility": "expert_audit", diff --git a/scripts/jyotish_engine.py b/scripts/jyotish_engine.py index 690b90cf..394ec4d8 100644 --- a/scripts/jyotish_engine.py +++ b/scripts/jyotish_engine.py @@ -5093,8 +5093,13 @@ def cmd_full_reading(args): try: from tajika import calc_tajika_yogas, calc_all_sahams - # Tajika Yogas(用本命盘行星经度) - tc_yogas = calc_tajika_yogas(planet_lons) + # Seven-planet Tajika candidates require actual instantaneous speed. + tajika_planets = { + name: {"longitude": item.get("degree_raw", item.get("lon")), "speed": item.get("speed")} + for name, item in planets.items() + if isinstance(item, dict) and name in {"Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"} + } + tc_yogas = calc_tajika_yogas(tajika_planets) report['modules']['tajika_yogas'] = tc_yogas # Sahams(特殊点)—— 需要出生时间 diff --git a/scripts/tajika_kernel.py b/scripts/tajika_kernel.py index 5f39bedf..14c01431 100644 --- a/scripts/tajika_kernel.py +++ b/scripts/tajika_kernel.py @@ -61,6 +61,19 @@ def calculate_tajika_interactions(planets: dict[str, dict[str, Any]]) -> dict[st "status": "partial", "nodes_excluded": True, "interactions": interactions, - "blocked_named_yogas": ["Nakta", "Yamaya", "Manahoo", "Ithasala chain"], - "boundary": "Only aspect/applying evidence is computed. Named Tajika yoga chains and verdicts remain blocked pending classic golden cases.", + "candidate_yogas": [ + { + "name": "Ithasala_candidate" if row["motion"] == "applying" else "Easarapha_candidate", + "planets": row["planets"], + "aspect": row["aspect"], + "residual": row["residual"], + "average_deeptamsa": row["average_deeptamsa"], + "motion": row["motion"], + "rule_source": "references/tajika-yoga-complete-guide.md#2.1-2.2", + "status": "partial", + } + for row in interactions + ], + "blocked_named_yogas": ["Nakta", "Yamaya", "Manahoo", "Kamboola", "Ithasala/Easarapha adjudication"], + "boundary": "Candidate labels are derived only from seven-planet aspect, Deeptamsa and applying/separating evidence. Full named-yoga chains and event verdicts remain blocked pending classic golden cases.", } diff --git a/tests/test_full_reading_saham_contract.py b/tests/test_full_reading_saham_contract.py index f771cfa3..db7fbd57 100644 --- a/tests/test_full_reading_saham_contract.py +++ b/tests/test_full_reading_saham_contract.py @@ -6,3 +6,10 @@ def test_full_reading_derives_saham_datetime_from_standard_chart_args() -> None: section = source[source.index("# ── Step 4.8: Tajika Yogas + Sahams"):source.index("# ── Step 4.9:")] assert "birth_dt = _birth_datetime_from_args(args)" in section assert "getattr(args, 'birth_datetime'" not in section + + +def test_full_reading_supplies_actual_speeds_to_tajika_kernel() -> None: + source = (Path(__file__).resolve().parents[1] / "scripts" / "jyotish_engine.py").read_text(encoding="utf-8") + section = source[source.index("# ── Step 4.8: Tajika Yogas + Sahams"):source.index("# ── Step 4.9:")] + assert '"longitude": item.get("degree_raw", item.get("lon"))' in section + assert '"speed": item.get("speed")' in section diff --git a/tests/test_tajika_kernel.py b/tests/test_tajika_kernel.py index e3ec6d16..14e41981 100644 --- a/tests/test_tajika_kernel.py +++ b/tests/test_tajika_kernel.py @@ -16,6 +16,9 @@ def test_kernel_detects_cross_sign_aspect_and_excludes_nodes(): pair = next(row for row in result["interactions"] if row["planets"] == ["Sun", "Moon"]) assert pair["aspect"] == 60.0 assert pair["motion"] == "applying" + candidate = next(row for row in result["candidate_yogas"] if row["planets"] == ["Sun", "Moon"]) + assert candidate["name"] == "Ithasala_candidate" + assert candidate["status"] == "partial" assert result["nodes_excluded"] is True assert all("Rahu" not in row["planets"] for row in result["interactions"])