diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index 4030d7bf..c8b20272 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -81,6 +81,7 @@ For large architecture or release work, also read: | ERR-048 | Candidate-time scanner assumed all documented D4/D24/D30 divisions were exposed by `jyotish_engine.py varga`; actual `--d4` failed at runtime. | mitigated 2026-07-12 | Candidate scans must record unsupported Varga flags as `unavailable_vargas`; only successfully computed D1/D9/D10 fields may drive local sensitivity output until a unified Varga contract exists. | | ERR-049 | PyJHora benchmark runner executed on `--help`, used a wrong repository-root path in `run_skill_baseline.py`, and failed when reused without pre-created output directories. | mitigated 2026-07-12 | Keep `tests/test_pyjhora_compare_cli.py`; require explicit `--build-local`, safe argparse help, correct repo root, and directory creation inside `run_sample()`. | | ERR-050 | Prashna CLI/API/UI could synthesize or accept a non-question chart; legacy Tajika/Saham/Sphuta/Kunda paths also exposed approximate values as usable evidence. | mitigated 2026-07-12 | Require backend Swiss `PrashnaContext` with question text/time/location/timezone; reject client planets/ascendant. Block legacy Sphuta/Kunda/Gulika/Panchavargiya and no-location Saham paths; keep seven-planet Tajika interactions partial until named-yoga golden cases and formula parity exist. | +| ERR-051 | Privacy redaction can replace executable numeric test fixtures with bare placeholder identifiers such as `REDACTED_YEAR`, causing `NameError` before a regression reaches its target. | observed 2026-07-12 | Public tests must use generic fixtures (for example 1990) or quoted placeholders only; run `rg -n "REDACTED_YEAR" tests` before release and repair executable occurrences. | ## Fragment Sweep Command Set diff --git a/scripts/adhana.py b/scripts/adhana.py index 1f2f1fbb..5e637722 100644 --- a/scripts/adhana.py +++ b/scripts/adhana.py @@ -12,7 +12,6 @@ from __future__ import annotations from typing import Dict, Any from jaimini import calc_special_lagnas_precise -from prashna import calc_gulika_simple from varga import calc_all_vargas @@ -45,7 +44,13 @@ def analyze_adhana_candidates(payload: Dict[str, Any]) -> Dict[str, Any]: asc_lon = float(payload["asc_lon"]) sun_lon = float(payload["sun_lon"]) moon_lon = float(payload["moon_lon"]) - gulika_lon = float(payload.get("gulika_lon", calc_gulika_simple(asc_lon, sun_lon, int(payload.get("weekday", 0))))) + if "gulika_lon" not in payload: + return { + "status": "blocked", + "reason": "exact_gulika_longitude_required_for_adhana_scaffold", + "blocked_layers": ["Gulika", "Adhana candidate points"], + } + gulika_lon = float(payload["gulika_lon"]) year = int(payload["year"]) month = int(payload["month"]) diff --git a/scripts/prashna.py b/scripts/prashna.py index c3b1dd6b..554eb6f0 100644 --- a/scripts/prashna.py +++ b/scripts/prashna.py @@ -810,10 +810,15 @@ def _planet_lon(planet_lons: Dict, planet: str, default: float = 0.0) -> float: return _norm(planet_lons.get(planet, default)) +class GulikaUnavailableError(RuntimeError): + """Raised when a caller asks for the retired approximate Gulika value.""" + + def calc_gulika_simple(asc_lon: float, sun_lon: float = 0.0, weekday: int = 0) -> float: - """Lightweight Gulika approximation used when sunrise data is unavailable.""" - weekday_offsets = [210, 180, 150, 120, 90, 60, 30] - return _norm((sun_lon or asc_lon) + weekday_offsets[weekday % 7]) + """Retired: exact Gulika needs sunrise, sunset, weekday and birth segment.""" + raise GulikaUnavailableError( + "approximate_gulika_removed_exact_day_segment_calculation_required" + ) def calc_arudha(asc_lon: float, planet_lons: Dict) -> Dict: @@ -862,7 +867,13 @@ def calc_sphutas(planet_lons: Dict, asc_lon: float = 0.0) -> Dict: def calc_life_sphutas(asc_lon: float, moon_lon: float, sun_lon: float, gulika_lon: float = 0.0) -> Dict: - gulika = _norm(gulika_lon or calc_gulika_simple(asc_lon, sun_lon)) + if not gulika_lon: + return { + "status": "blocked", + "reason": "exact_gulika_longitude_required_for_life_sphutas", + "blocked_layers": ["Gulika", "Prana", "Deha", "Mrityu"], + } + gulika = _norm(gulika_lon) prana = _norm(asc_lon * 5 + gulika) deha = _norm(moon_lon * 8 + gulika) mrityu = _norm(gulika * 7 + sun_lon) diff --git a/scripts/technique_registry.py b/scripts/technique_registry.py index 013dfc72..b6896788 100644 --- a/scripts/technique_registry.py +++ b/scripts/technique_registry.py @@ -367,8 +367,8 @@ def _register_existing_techniques(): TechniqueLevel.L2_STANDARD, TechniqueStatus.STABLE, module_path="synastry.py", compute_func="calc_synastry"), TechniqueSpec("prashna", "问事系统", TechniqueCategory.PRASHNA, - TechniqueLevel.L2_STANDARD, TechniqueStatus.BETA, - module_path="prashna.py", compute_func="cast_prashna"), + TechniqueLevel.L2_STANDARD, TechniqueStatus.WIP, + module_path="prashna_context.py", compute_func="build_prashna_context"), # L3 高级技法(部分已有) TechniqueSpec("vimsopaka", "20分力量", TechniqueCategory.NATAL, diff --git a/tests/test_adhana.py b/tests/test_adhana.py index 7c2d37c6..99f34b43 100644 --- a/tests/test_adhana.py +++ b/tests/test_adhana.py @@ -20,7 +20,7 @@ def sample_birth_payload(): "moon_lon": 311.78995555555554, "gulika_lon": 256.581676, "weekday": 6, - "year": REDACTED_YEAR, + "year": 1990, "month": 4, "day": 17, "hour": 14, diff --git a/tests/test_api_server_security.py b/tests/test_api_server_security.py index a701f10f..c4fb0362 100644 --- a/tests/test_api_server_security.py +++ b/tests/test_api_server_security.py @@ -954,7 +954,7 @@ def test_prashna_advanced_legacy_functions_exist() -> None: assert prashna.cast_prashna('2026-06-22 12:00', 28.6, 77.2)['status'] == 'blocked' assert prashna.calc_arudha(15.5, planet_lons)['arudha_house'] assert prashna.calc_sphutas(planet_lons, 15.5)['status'] == 'blocked' - assert prashna.calc_life_sphutas(15.5, 70, 10)['signal'] + assert prashna.calc_life_sphutas(15.5, 70, 10)['status'] == 'blocked' assert prashna.calc_sahams(planet_lons, 15.5)['status'] == 'blocked' assert prashna.analyze_lost_item(planet_lons, 15.5)['summary'] assert prashna.kunda_verify(15.5)['status'] == 'blocked'