fix(logic): pomijaj trywialne aspekty par sztywnych (NN/SN)
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m44s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 27s
Testy / Kontrola składni wszystkich warstw (push) Successful in 23s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m39s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 25s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 22s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m44s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 27s
Testy / Kontrola składni wszystkich warstw (push) Successful in 23s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m39s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 25s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 22s
Węzły księżycowe są z definicji w dokładnej opozycji (SN = NN + 180°), więc wpis "North Node opposition South Node orb 0.00°" nie niósł żadnej informacji astrologicznej — zaśmiecał /chart/positions i UI horoskopu, a w generatorze promptów LLM zjadał budżet znaków i mógł zostać wzięty przez model za realne świadectwo. - aspects.py: RIGID_PAIRS + pomijanie takich par w find_aspects; struktura zbioru pozwala dopisać kolejne pary definicyjne, - aspekty węzłów do pozostałych obiektów bez zmian, - testy: jawne sprawdzenie braku pary NN/SN (jednostkowo i na horoskopie referencyjnym). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,18 @@ LUMINARIES = {"Sun", "Moon"}
|
||||
DEFAULT_ORB = 8.0
|
||||
LUMINARY_BONUS = 2.0
|
||||
|
||||
# Pary sztywno powiązane definicyjnie — kąt między nimi wynika z samej definicji
|
||||
# punktu, nie z układu nieba (SN = NN + 180°). Aspekt taki zawsze wychodzi
|
||||
# dokładny (orb 0,00°) i nie niesie żadnej informacji astrologicznej, więc
|
||||
# wycinamy go z wyników: zaśmieca listę w UI i zjada budżet promptu do LLM.
|
||||
RIGID_PAIRS = frozenset({
|
||||
frozenset({"North Node", "South Node"}),
|
||||
})
|
||||
|
||||
|
||||
def _is_rigid(name_a: str, name_b: str) -> bool:
|
||||
return frozenset({name_a, name_b}) in RIGID_PAIRS
|
||||
|
||||
|
||||
def separation(a: float, b: float) -> float:
|
||||
"""Najmniejsza separacja kątowa [0,180]."""
|
||||
@@ -56,12 +68,16 @@ def find_aspects(
|
||||
|
||||
Zwraca listę aspektów głównych; gdy znane są prędkości, każdy aspekt ma
|
||||
applying (bool) i skrót 'as': 'A'/'S' (aplikacyjny/separacyjny).
|
||||
|
||||
Pary z RIGID_PAIRS (np. NN/SN) są pomijane — ich kąt jest definicyjny.
|
||||
"""
|
||||
out: list[dict] = []
|
||||
n = len(positions)
|
||||
for i in range(n):
|
||||
for j in range(i + 1, n):
|
||||
a, b = positions[i], positions[j]
|
||||
if _is_rigid(a["name"], b["name"]):
|
||||
continue
|
||||
la, lb = a.get("decimal"), b.get("decimal")
|
||||
if la is None or lb is None:
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Testy aspektów (LOG-06) — czysta matematyka."""
|
||||
from app.engine.aspects import find_aspects, separation
|
||||
from app.engine.aspects import RIGID_PAIRS, find_aspects, separation
|
||||
|
||||
|
||||
def test_separation_wraparound():
|
||||
@@ -55,6 +55,28 @@ def test_separating_when_moving_apart():
|
||||
assert a["applying"] is False and a["as"] == "S"
|
||||
|
||||
|
||||
def test_rigid_pair_nodes_not_reported():
|
||||
# SN = NN + 180° z definicji — trywialna opozycja, nie aspekt
|
||||
pos = [
|
||||
{"name": "North Node", "decimal": 42.0, "speed": -0.053},
|
||||
{"name": "South Node", "decimal": 222.0, "speed": -0.053},
|
||||
{"name": "Sun", "decimal": 42.5, "speed": 0.96},
|
||||
]
|
||||
pairs = {frozenset((a["obj1"], a["obj2"])) for a in find_aspects(pos)}
|
||||
assert frozenset({"North Node", "South Node"}) not in pairs
|
||||
# aspekty węzłów do innych obiektów zostają nietknięte
|
||||
assert frozenset({"Sun", "North Node"}) in pairs
|
||||
assert frozenset({"Sun", "South Node"}) in pairs
|
||||
|
||||
|
||||
def test_no_rigid_pairs_in_real_chart(own_engine, reference_moment):
|
||||
from app.engine.chart import build_chart
|
||||
|
||||
chart = build_chart(own_engine, reference_moment)
|
||||
found = [a for a in chart["aspects"] if frozenset((a["obj1"], a["obj2"])) in RIGID_PAIRS]
|
||||
assert not found, f"trywialne aspekty par sztywnych w horoskopie: {found}"
|
||||
|
||||
|
||||
def test_no_as_flag_without_speeds():
|
||||
pos = [{"name": "Sun", "decimal": 0.0}, {"name": "Moon", "decimal": 2.0}]
|
||||
assert "as" not in find_aspects(pos)[0]
|
||||
|
||||
Reference in New Issue
Block a user