diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index 322546ce..3963d1f2 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -88,6 +88,7 @@ For large architecture or release work, also read: | ERR-055 | The full-reading path called `calc_all_sahams()` without lat/lon/tz, so an otherwise computable Swiss day/night context was silently blocked. | resolved 2026-07-14 | Pass the calculation arguments' lat/lon/tz into the Saham layer; keep Saham formula maturity `partial` until oracle parity exists. | | ERR-056 | A WorkBuddy checkout of the same remote diverged substantially from the active source branch and can be mistaken for a mergeable mirror. | active | Read `whole_machine_fragment_sweep_2026_07_14.md`; do not copy or merge it without explicit commit-level review on a separate branch. | | ERR-057 | The release quality profile checked untracked files but did not execute the privacy AST scan or the real Chromium report-isolation probe. | mitigated 2026-07-14 | `release_hygiene_check()` now requires `public_release_privacy_scan.py --json` and `report_renderer_isolation_poc.py --strict`; parity manifest validation also runs as a contract check. | +| 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. | ## Fragment Sweep Command Set diff --git a/scripts/tajika.py b/scripts/tajika.py index fb767deb..74b9c80e 100644 --- a/scripts/tajika.py +++ b/scripts/tajika.py @@ -64,7 +64,15 @@ def _calc_formula_saham( first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed) second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed) third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed) - return (third + (first - second)) % 360 + result = (third + (first - second)) % 360 + # references/saham_rules.json: add one sign when Ascendant is not on the + # forward zodiacal arc from the first formula point to the second. + ascendant_between_points = ( + first <= asc_lon <= second + if first <= second + else asc_lon >= first or asc_lon <= second + ) + return result if ascendant_between_points else (result + 30.0) % 360 def calc_muntha(birth_asc_idx: int, age: int) -> Dict: diff --git a/tests/test_tajika.py b/tests/test_tajika.py index 601e2174..82462b62 100644 --- a/tests/test_tajika.py +++ b/tests/test_tajika.py @@ -262,6 +262,19 @@ class TestSahams: result = calc_all_sahams(planet_lons, asc, datetime(1990, 6, 15, 12, 0), lat=39.9042, lon=116.4074, tz=8) assert abs(result['karma_saham']['longitude'] - expected) < 0.01 + def test_formula_saham_applies_reference_add_30_exception(self): + from tajika import _calc_formula_saham + + # Asc 50 is outside the forward 100 -> 200 zodiacal arc. + result = _calc_formula_saham( + 'Punya_Saham', + {'Sun': 200.0, 'Moon': 100.0}, + 50.0, + True, + {}, + ) + assert result == 340.0 + def test_is_faster_moon_vs_sun(self): assert _is_faster('Moon', 'Sun') assert _is_faster('Mercury', 'Jupiter')