test: framework porównania domów z wyrocznią + DWA błędy, które od razu wykrył
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 14s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
build / build (push) Successful in 19s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m29s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 13s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s

Etap 0 planu egzotycznych systemów domów: zanim zaczniemy implementować Placidusa
i spółkę, potrzebujemy narzędzia, które powie, czy wynik jest poprawny. Błąd
w domach jest CICHY — wykres wygląda dobrze, tylko planety siedzą w złych domach.

FRAMEWORK (tests/oracle): liczenie cuspów jako funkcja (RAMC, ε, φ), Swiss
Ephemeris jako wyrocznia. Trzy decyzje projektowe, które okazały się kluczowe:
- IZOLACJA: obu implementacjom podajemy TE SAME wejścia przez swe_houses_armc.
  Porównywanie „naszego horoskopu" z „horoskopem swissepha" mieszałoby różnice
  czasu gwiazdowego i ε z błędami domów — utonęlibyśmy w fałszywych alarmach.
  Czas gwiazdowy i ε mają własny test.
- KRYTERIUM to liczba przypadków powyżej tolerancji (1″), max odchylenie I MIEJSCE,
  a nie procent zgodności. Procent ukrywa kształt błędu: „97%" nie odróżnia szumu
  zmiennoprzecinkowego od rogu dziedziny, w którym mylimy się o 30°.
- GRANICE PER DATA: koło podbiegunowe nie jest stałą 66,56° — zależy od ε, które
  zmienia się z datą (23,75° w 370 p.n.e.), więc przesuwa się o ~0,3°.

Uruchomiony na kodzie uchodzącym za poprawny, w PIERWSZYM przebiegu znalazł dwa
realne błędy:

1. ASCENDENT O 180° ZA KOŁEM PODBIEGUNOWYM. `atan2` wybierał niewłaściwy punkt
   przecięcia ekliptyki z horyzontem — zwracaliśmy Descendent. Planety lądowały
   w PRZECIWNYCH domach dla całej północnej Skandynawii (Tromsø, Rovaniemi,
   Murmańsk), na ~11% przypadków przy tych szerokościach. Rozstrzyga położenie
   względem MC: punkt wschodzący leży w półkolu (0°,180°) na wschód od MC.
2. NIEDETERMINIZM WHOLE SIGN NA GRANICY ZNAKU. Ascendent o włos od granicy
   (359,999999999976 vs 1e-10 — ta sama wartość, różne strony) przerzucał dom I
   o 30°. Ten sam horoskop na innej maszynie dawał inny wynik. Przyciąganie do
   granicy przy 1e-9° (3,6 mikrosekundy łuku — poniżej realnej dokładności danych).

Oba mają testy regresji w zwykłej suicie, więc są łapane też bez swissepha.

Po poprawkach: build 0 przekroczeń, sweep 20 000 przypadków = 760 000 porównań,
max odchylenie 0.000000000°. Istniejące suity bez regresji (logika 277+3, prez. 249).

CI: krok BLOKUJĄCY w jobie swisseph-image, odpalany wewnątrz obrazu silnika B
z zamontowaną warstwą logiczną. pyswisseph zostaje wyłącznie wyrocznią testową —
nie wchodzi do zależności produktu, izolacja z LOG-27 nienaruszona.

Dodane `cusps_for(ramc, eps, lat, system)` — kanoniczne wejście, w które Etap 1
będzie tylko dopisywał kolejne systemy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #60.
This commit is contained in:
2026-08-04 18:03:08 +02:00
parent e3114f3e7c
commit 86a0f16f9e
7 changed files with 571 additions and 4 deletions
+36
View File
@@ -1,4 +1,6 @@
"""Testy osi i domów — czysta matematyka (LOG-05, bez efemeryd)."""
import pytest
from app.engine import houses as H
# RAMC i ε policzone Skyfieldem dla horoskopu referencyjnego (30.04.1984, Warszawa)
@@ -39,3 +41,37 @@ def test_assign_house_whole_sign():
cusps = H.cusps(112.18, 352.59, H.WHOLE_SIGN) # dom 1 = Rak (90120°)
assert H.assign_house(100.0, cusps) == 1 # w Raku
assert H.assign_house(40.0, cusps) == 11 # Byk -> 11. dom
# ── regresje wykryte przez porównanie z wyrocznią (tests/oracle) ──────────
def test_ascendant_branch_beyond_polar_circle():
"""REGRESJA: powyżej koła podbiegunowego `atan2` wybierał NIEWŁAŚCIWY punkt
przecięcia ekliptyki z horyzontem — zwracaliśmy Descendent zamiast Ascendenta,
czyli błąd o 180°. Planety lądowały w PRZECIWNYCH domach dla całej północnej
Skandynawii. Rozstrzyga położenie względem MC: punkt wschodzący leży zawsze
w półkolu (0°,180°) na wschód od MC."""
eps = 23.4393
for lat in (67.0, 70.0, 85.0, -70.0):
for ramc in (60.0, 100.0, 200.0, 270.0, 300.0):
asc = H.compute_asc(ramc, eps, lat)
mc = H.compute_mc(ramc, eps)
offset = (asc - mc) % 360.0
assert 0.0 <= offset <= 180.0, (
f"Asc poza półkolem wschodzącym: lat={lat} ramc={ramc} offset={offset:.4f}")
def test_whole_sign_is_deterministic_at_sign_boundary():
"""REGRESJA: Ascendent o włos od granicy znaku (359,999999999976 vs 1e-10 —
ta sama wartość, różne strony) przerzucał cały dom I o 30°. Ten sam horoskop
na innej maszynie dawał inny wynik."""
below = H.cusps(359.999999999976, 270.0, H.WHOLE_SIGN)
above = H.cusps(1e-10, 270.0, H.WHOLE_SIGN)
assert below[0] == above[0] == 0.0, (below[0], above[0])
def test_polar_circle_moves_with_obliquity():
"""Granica dziedziny Placidusa/Kocha NIE jest stałą 66,56° — zależy od ε,
a ε zmienia się z datą. Testy brzegowe muszą ją liczyć per data."""
assert H.polar_circle(23.4393) == pytest.approx(66.5607, abs=1e-4) # dziś
assert H.polar_circle(23.747) == pytest.approx(66.253, abs=1e-4) # 370 p.n.e.