- Root cause: rel_to_any() used associated(p,o) with bidirectional aspects (aspect(p,o) OR aspect(o,p)), but PyJHora only checks one-way (trik lord -> L1, malefic -> L1, maraka -> target)
- Fixed v144/v147/v148/v149/v152 to use one-way aspect() checks matching PyJHora source
- v144: L7 aspects L1/L12 (one-way)
- v147/v148: maraka aspects L1 (one-way)
- v149: trik lord aspects L1, malefic aspects L1 (one-way, both were already correct for second part)
- v152: maraka aspects malefic in lagna (one-way)
- v145/v150/v151 were already correct (used aspect() directly)
- Corrected test expectation for chart that genuinely has dharidhra via v149
- Added proper False-no-variant-triggers test case (Cancer asc)
- Hardcoded tests: 10/10 passed
- yoga_test_suite.json: 60 rules x True/False test cases generated
from the 60-chart benchmark (40 rules have both True+False)
- run_yoga_test_suite.py: test runner, 97/100 passed, 3 known FN
residuals (bahu_puthra, dehasthoulya, grihanasa)
- Inspired by PyJHora's hardcoded _from_planet_positions methodology
9 tests total, 8/9 passing. 1 failure is a test design issue
(dharidhra False case triggers v149 due to Saturn aspecting Sun).
Inspired by PyJHora's _from_planet_positions test methodology.
- Fix: _compute_one_chart.py JD calculation used PyJHora's
gregorian_to_jd() which ALWAYS returns noon JD regardless of time.
Changed to swe.julday() with correct UTC hour.
- Impact: All 60 test charts with non-noon birth times had wrong
ascendants and house assignments (e.g. Einstein went from
Scorpio Asc -> Gemini Asc, matching Swiss Ephemeris)
- Cross-validated: Swiss Ephemeris (industry gold standard) and
xalen-ephemeris (Rust, 5M chart oracle test)
- New baseline: F1=93.50% (Precision=93.82%, Recall=93.18%)
- Regenerated: standard_test_charts.json, planet_positions_60.json,
validation_logic_report.json
- Quality gate: compile/json/audit/BPHS/pytest/golden all passed
Bug #1: Ascendant degree 76.67° (out of range 0-30°)
- Root cause: cmd_chart stored absolute longitude (76.67°) in 'degree' field
instead of degree-within-sign (16.67°)
- Fix: 'degree' now stores degree_in_sign (16.67°), added 'lon' field for
absolute longitude used by downstream calculations
- Updated all 7 downstream reads (cmd_bhava_chalit, cmd_chart_rulership,
cmd_yoga, cmd_dignity, cmd_solar_return, cmd_d9_expanded, cmd_full_reading)
to use 'lon' instead of 'degree' for internal calculations
- solar_return.py: same fix for sr['ascendant'] reads
Bug #2: Dasha returns N/A in full-reading
- Root cause: cmd_full_reading passed transit_date as 'today' to cmd_dasha,
but cmd_dasha looked for args.today which was None
- Fix: today_str = getattr(args, 'transit_date', None) or getattr(args, 'today', None)
- Result: Dasha now correctly shows Moon Maha / Jupiter Antar for Einstein
Bug #3: Nakshatra returns N/A in full-reading
- Root cause: nakshatra_full_report nested moon_nakshatra under 'summary'
sub-dict; cmd_full_reading expected it at top level
- Fix: Added moon_nakshatra, moon_nakshatra_lord, moon_pada as top-level
fields in nakshatra_full_report return value
- Result: moon_nakshatra now correctly shows Jyeshtha Pada 2
Bug #4: Yoga detects 0 in full-reading
- Root cause: cmd_yoga returned 'yogas' list but cmd_full_reading expected
'detected_yogas' key
- Fix: cmd_yoga return dict now includes both 'yogas' and 'detected_yogas'
- Result: 15 yogas detected (Raja, Malavya, Voshi, Kemadruma, etc.)
Verification:
- py_compile: all pass
- audit_capabilities --mode validate: valid=True, warnings=0, problems=0
- Einstein full-reading: 45 modules, 0 errors, status=complete
- SKILL.md: version 6.0.21 -> 6.0.24, header v6.0.23 -> v6.0.24-mcp-server
- mcp_server.py: strict_workflow was calling 'jyotish_engine.py strict-workflow'
which does not exist as an argparse subcommand. Rewrote to:
1. Keyword-based route detection (career/relationship/finance/timing/general)
2. Call existing 'full-reading' subcommand (guaranteed to exist)
3. Inject 'routing' metadata into response for client-side filtering
- py_compile + audit_capabilities --mode validate: all pass
- warnings=0, problems=0
Narayana Dasha is Parashara's rashi-based dasha system, complementary to
Vimshottari (nakshatra-based). Algorithm: start from Lagna, each sign's
dasha years = count from the sign to its lord's sign (zodiacal order).
Implementation:
- scripts/narayana_dasha.py: core calculation module
- calc_narayana_mahadasha(): 12-sign sequence from Lagna
- calc_narayana_antardasha(): sub-periods within each MD
- get_current_narayana_dasha(): locate current period by age
- scripts/cmd_narayana_dasha.py: CLI subcommand
- jyotish_engine.py: Step 4.13 in full-reading + narayana-dasha subparser
Verified with Leo Asc chart: total cycle 88 years, current Sagittarius MD
(31-38 yrs, 5H/creativity) complements Vimshottari Saturn MD.
Files: +2 new (narayana_dasha.py, cmd_narayana_dasha.py), +4 modified
Bug: v6.0.18's solar_return_full_report returned early when calc_solar_return_chart
failed (no swisseph), skipping Muntha entirely.
Fix:
- Added _estimate_sun_sign() with lookup table for sun sign from birth date
- Refactored solar_return_full_report to use degraded mode:
- Muntha + Year Lord always computed (using lookup table sun sign)
- Tajika/Sahams/Tri-Pataka/Mudda gracefully skipped with notes
- Added _SUN_SIGN_LOOKUP table covering 1950-2100, \u00b11 sign accuracy
Files: solar_return.py, SKILL.md, CHANGELOG.md, technique_registry.json