fd79513ce2f6f86a06110e5b4b96abfaf6bc0532
16 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fd79513ce2 |
astrololo: ekrany i eksport jako moduły, katalog z rejestracji (3/5)
Testy / Testy warstwy logicznej (silnik) (pull_request) Failing after 4s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Failing after 3s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Failing after 3s
Testy / Testy astrodemo (pull_request) Failing after 3s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 2s
Testy / Kontrola składni wszystkich warstw (pull_request) Failing after 3s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Failing after 3s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Failing after 3s
Testy / Testy astrodemo (push) Failing after 3s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 2s
Testy / Kontrola składni wszystkich warstw (push) Failing after 3s
build / build (push) Successful in 9s
Trzeci z pięciu kroków. Zmieniłem jego zakres wobec planu i warto wiedzieć dlaczego: pierwotnie miała to być deduplikacja pięciu kopii link_crypto.py, ale po zrobieniu PR 2 widać, że astroklienta blokuje co innego — ekrany, których nie ma mieć, siedzą wewnątrz jednego main.py. Deduplikacja kryptografii jest realnym długiem, ale niczego nie blokuje. PODZIAŁ. main.py (1023 linie) rozpadł się na podstawa.py (wspólne obiekty i pomocnicy), jedenaście modułów w app/ekrany/ i main.py, który jest już samym ZŁOŻENIEM: lista importów JEST definicją produktu. Podział zrobiony mechanicznie, z osobnym sprawdzeniem, że żadna sekcja nie wołała pomocnika z innej (nie wołała). KATALOG Z REJESTRACJI. Dotąd wszystkie funkcje były wypisane w features.py, więc obraz produktu, który części z nich nie ma, i tak niósł ich nazwy — spis funkcji, których nie ma jak włączyć. Teraz ekran zgłasza siebie, swoje trasy i swoje zasoby przy imporcie własnego modułu, a features.py nie wymienia ani jednego ekranu. Kolejność w nawigacji jest jawna (`kolejnosc`), żeby nie rządziła nią kolejność importów. To samo dotyczy nawigacji administratora: odsyłacz do ekranu kont był wpisany na sztywno w base.html, więc w węższym produkcie zostawał martwy link i nazwa ekranu, którego nie ma. EKSPORT JAKO MODUŁ. Zgodnie z ustaleniem eksport jest funkcją administracyjną, więc musi dać się usunąć. app/moduly/eksport/ zabiera arkusz, trasę PDF-a i akcję formularza. „Można, ale nie temu kontu" i „nie ma takiej możliwości" to dwie różne gwarancje, a eksport wynosi najwięcej treści baz naraz. MOST ODKRYWA MODUŁY. Skoro modułów jest więcej niż jeden, most nie może ich znać z nazwy — nazwa nieobecnego modułu jechałaby do obrazu, w którym go nie ma. Przechodzi więc po podkatalogach app/moduly/ i pyta każdy, co wnosi. Katalog generowania przeniesiony z app/dodatki na app/moduly/dodatki. ZNALEZIONE PRZY OKAZJI. Po wydzieleniu eksportu okazało się, że jego ścieżki SZCZĘŚLIWEJ nie sprawdzał żaden test — badano wyłącznie odmowę dla konta bez uprawnienia. Moduł dostaje zależności z wywołania montującego, więc brak jednej z nich wyszedłby dopiero przy pierwszym kliknięciu. Dopisany test funkcjonalny (realny arkusz, sprawdzany aż do nagłówka ZIP-a) i brakujące zależności. Cztery komentarze w plikach współdzielonych wymieniały zakładkę „Skompiluj", w tym wheelzoom.js ze wzmianką o „przyszłej zakładce" — to samo zgłoszenie, które audyt podnosił wcześniej. TEST ZŁOŻENIA. Buduje węższy produkt NAPRAWDĘ: kopiuje drzewo, usuwa cztery ekrany i oba moduły, uruchamia aplikację w OSOBNYM PROCESIE (importy są zapamiętywane, więc sprawdzanie tego w procesie, który moduł już zaimportował, dawałoby wynik fałszywie pozytywny) i sprawdza, że wstaje, że zachowane ekrany oddają 200, że usunięte oddają 404 (nie 403 i nie 500), że katalog opisuje ten obraz, i że w nawigacji nie ma martwych odsyłaczy. Napisałem najpierw ostrzejszy test — „nazwa ekranu nie pada poza jego modułem" — i go wyrzuciłem: zgłaszał wzmianki o Horoskopie w plikach współdzielonych, choć astroklient Horoskop MA. Ślad ma znaczenie wyłącznie wobec konkretnego złożenia, więc sprawdzenie należy do produktu, nie do mechanizmu. Testy: presentation 368, logic 342, data 37, render 41. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
62d6f9d4d5 |
astrololo: generowanie tekstu jako moduł odłączalny (2/5)
build / build (push) Failing after 2s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Failing after 3s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Failing after 3s
Testy / Testy astrodemo (push) Failing after 3s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 2s
Testy / Kontrola składni wszystkich warstw (push) Failing after 3s
Drugi z pięciu kroków budowy trzech produktów. Czysty refaktor — zachowanie
aplikacji się nie zmienia, liczba testów rośnie tylko o nowe.
DLACZEGO WARUNEK W SZABLONIE NIE WYSTARCZA. Dotąd generowanie było chowane przez
`{% if can(request, 'ai') %}`. To sprawia, że funkcji nie WIDAĆ, ale nie że jej
NIE MA: plik szablonu dalej leży w obrazie i dalej zawiera jej nazwy, więc `grep`
po kontenerze pokazuje wszystko, czego warunek nie pokazał na ekranie. Astroklient
ma nie mieć śladu, nie mieć wyłączoną funkcję — więc granica musi być KATALOGIEM.
CO SIĘ PRZENIOSŁO do app/dodatki/: dwa szablony, pięć plików statycznych, trasa
strumienia, cztery metody klienta warstwy logicznej, wpis w katalogu funkcji,
wpisy tras i zasobów, akcje formularza i reguły CSS nazywające funkcję
(`textarea.prompt`).
JAK APLIKACJA O TO PYTA. Neutralny most `app/rozszerzenia.py` odpowiada wyłącznie
na pytanie „czy coś jest podpięte i co wnosi". Sam nie wymienia ani jednej nazwy —
pilnuje tego osobny test, bo most jest w KAŻDYM obrazie. Dlatego też katalog
nazywa się `dodatki`, a nie `ai`: nazwa w instrukcji importu byłaby dokładnie tym
śladem, którego wydzielanie ma się pozbyć.
TRZY MIEJSCA, KTÓRE OKAZAŁY SIĘ TRUDNE:
1. Pola formularza. FastAPI czyta je z SYGNATURY, a wspólny handler nie może
wymieniać `prompt_budget` ani `llm_provider`. Rozwiązane zależnością: moduł
deklaruje własne pola u siebie, handler wie tylko, że dostaje słownik.
2. Klient warstwy logicznej jest wspólny, więc metody `prompt`/`horoscope`/
`llm_models` musiały z niego wyjść. Wspólny klient daje teraz samą drogę
w dół (`wywolaj`, `pobierz`, `strumien`) — z szyfrowaniem łącza i tokenem
międzywarstwowym; co nią pojedzie, jest sprawą modułu.
3. Podtytuł ekranu Skompiluj miał wariant „z AI" i wariant bez. Zamiast warunku
jedno zdanie prawdziwe niezależnie od tego, jakie moduły są w obrazie.
DOCKERFILE. `COPY . .` wnosiło do obrazu także testy — a plik testowy nazywa
funkcje wprost. Teraz wchodzi wyłącznie `app/`.
TEST GRANICY. `test_ai_tylko_w_module.py` przechodzi po wszystkim poza modułem
i szuka siedemnastu słów. Znalazł dwanaście resztek, których nie widziałem:
komentarze w main.py i security.py, przykład `models.js` w komentarzu features.py,
komentarze w compile.js i w trzech arkuszach, oraz regułę `textarea.prompt`,
która przy rozbijaniu CSS trafiła do arkuszy ekranów zamiast do modułu.
Ma też kontrolę pozytywną (te słowa MAJĄ padać w module — inaczej test
przechodziłby, gdyby funkcję wydrążono) i test odłączalności mostu.
SPRAWDZONE NA KOPII BEZ MODUŁU: aplikacja startuje, wszystkie ekrany oddają 200,
katalog ma 13 funkcji zamiast 14, trasy strumienia nie ma, spreparowane
`action=prompt` wraca do akcji domyślnej bez śladu, a `grep` po drzewie nie
znajduje ani jednego ze słów.
Testy: presentation 362, logic 342, data 37, render 41.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
320a0ab24e |
PRE-27: pełne ukrycie niedostępnych funkcji (paranoja)
build-render / build (push) Failing after 8s
build-swisseph / build (push) Successful in 9s
build / build (push) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 5s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Failing after 4s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Failing after 4s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 6s
Testy / Kontrola składni wszystkich warstw (push) Failing after 4s
Ukrywanie jest teraz nadrzędne wobec wygody i czytelności komunikatów. Persona: konto z uprawnieniami files + files_input, zatrudnione wyłącznie do wgrywania plików. Nie ma się dowiedzieć, po co je wgrywa ani co program będzie robił — bo to rozgada. Audyt sześciu kanałów wycieku (statyki, HTML, sondowanie HTTP, ekran plików, odpowiedzi JSON i błędy, pozostałe warstwy) potwierdził 26 wycieków, każdy odtworzony uruchomionym kodem i zweryfikowany adwersarialnie. Ani jeden nie był przyciskiem. ZASÓB JEST CZĘŚCIĄ FUNKCJI /static/ omijało CAŁĄ bramkę (PUBLIC_PREFIXES), więc każdy skrypt i arkusz pobierał ktokolwiek, także niezalogowany, pod zgadywalnym adresem — a ich treść wymienia ekrany, dostawców modeli i przeznaczenie plików. Ruch ten nie trafiał przy tym ani do dziennika, ani pod limit żądań, więc wyciek był niewidoczny. Zasoby idą teraz trasą z bramką; każdy ma w features.STATIC uprawnienie swojego ekranu. Publiczny został jeden base.css, bo potrzebuje go ekran logowania. KOMENTARZ NIE JEDZIE NA DRUT Komentarze w CSS/JS opisywały funkcje pełnymi zdaniami po polsku — łącznie z „Wstrzymane widzi tylko administrator", czyli i mechanizmem kwarantanny, i istnieniem konta o wyższych uprawnieniach. _asset_body() usuwa je przy serwowaniu; w repozytorium zostają. styles.css rozbity na base.css + arkusz na ekran + x-ai.css. Jeden plik z wszystkimi selektorami był spisem treści programu. Podział zrobiony mechanicznie, z osobnym sprawdzeniem, że żaden ekran nie stracił reguły. base.html ładował skrypty kosmogramu na KAŻDEJ stronie — konto mające wyłącznie Pliki pobierało je przy wejściu na swój jedyny ekran, razem ze wzmianką o „przyszłej zakładce". Teraz dokłada je ekran, który ich używa. RÓŻNICA JEST INFORMACJĄ Komunikat po wgraniu pliku różnił się zależnie od wyniku walidacji — czyli był wyrocznią do odgadywania reguł, które ma znać tylko administrator — i mówił wprost, że plik „musi zatwierdzić administrator". Teraz jest jeden, ten sam. _logic_error wypisywał na ekran nazwę trasy, nazwę podsystemu, nazwę gałęzi rozwojowej i wewnętrzny host:port. Jedno zdanie dla wszystkich awarii, szczegóły do dziennika. Odsiew w jednym punkcie, nie w siedemnastu wywołaniach. Ponadto: stopka nie ogłasza architektury, /health nie nazywa warstwy, konto bez ekranów dostaje 404 zamiast tłumaczenia, ekran plików mówi o plikach zamiast o „bazach interpretacyjnych", klasy .house-warning i .account-card przemianowane na neutralne, a logic/data/render/engine-swisseph nie wystawiają już /docs ani /openapi.json i nie publikują portów na hoście. ZAPORA SŁOWNIKOWA test_slownik_zakazany.py nie sprawdza miejsc, tylko przechodzi wszystko, co dane konto może pobrać, i szuka słów, które nie mają prawa paść (87 pozycji dla tej persony). Nazwy funkcji, adresy ekranów i nazwy zasobów biorą się wprost z katalogu, więc nowa funkcja obejmuje się sama. Kontrola pozytywna pilnuje, żeby test nie przechodził dlatego, że program jest pusty. Sprawdzone: zapora puszczona na treść sprzed poprawek daje 16 trafień na samym styles.css i łapie każdy ze zneutralizowanych komunikatów. 358 testów zielonych, ekrany obejrzane w przeglądarce. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1d0ff2f72e |
PRE-27: generowanie tekstu przez model znika bez uprawnienia
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m20s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 5s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 5s
build / build (push) Successful in 1m39s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m32s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 6s
Wymaganie było mocniejsze niż schowanie przycisku: po niedostępnej funkcji nie może zostać śladu w źródle strony. Największy wyciek nie był przyciskiem — _prompt_block wstrzykiwał w stronę CAŁY katalog modeli jako JSON (dostawcy, nazwy modeli, rozmiary okien kontekstu), na każdym ekranie z generowaniem, niezależnie od uprawnień konta. Druga dziura była głębsza: handlery nie sprawdzały nic. Trasa /interpret musi być dostępna dla konta z Interpretacjami, więc granica przebiega WEWNĄTRZ niej, po polu `action` — spreparowany formularz z action=prompt generował tekst, a action=export pobierał arkusz, mimo że szablon chował oba przyciski. Akcja bez uprawnienia wraca do akcji domyślnej ekranu zamiast dawać błąd: komunikat „brak uprawnień do generowania" sam w sobie mówiłby, że taka funkcja istnieje. Ślady wycięte także tam, gdzie nie były kontrolką: znaczniki natalNote/ reportNatal, pliki models.js/progress.js/natal.js/predictions.js oraz podtytuł ekranu Skompiluj, który wymieniał interpretację od AI z nazwy. 12 testów; 9 z nich pada na kodzie sprzed poprawki (sprawdzone przez cofnięcie zmian w app/). Kontrola pozytywna pilnuje, żeby nie przechodziły dlatego, że generowanie jest zepsute dla wszystkich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a833965909 |
feat(bezpieczeństwo): konta z uprawnieniami do zakładek i funkcji (PRE-27)
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m58s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m32s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 3m37s
Testy / Kontrola składni wszystkich warstw (push) Successful in 9s
build / build (push) Successful in 18s
Ekran „Konta" dla administratora: zakładanie, kasowanie i nadawanie uprawnień. Zestaw funkcji zależy od konta, a konto ograniczone widzi program KOMPLETNY — tylko mniejszy. PODZIAŁ NA GRUPY. Ekrany to zakładki (7), bo zakładka jest naturalną jednostką — to ją widać w nawigacji. Rozszerzenia to POZIOMY ZŁOŻONOŚCI wewnątrz ekranów: porównanie systemów domów, wykresy dodatkowe, obliczenia zaawansowane, generowanie tekstu przez model (kosztuje pieniądze) i eksport plików. Konto bez porównania domów dostaje horoskop w Whole Sign i nie wie, że systemów jest trzynaście. NIC NIE ZDRADZA, ŻE JEST WIĘCEJ: - brak pozycji w menu zamiast pozycji wyszarzonej, - 404 zamiast 403 — odmowa z powodem sama mówi, że coś tam jest, - rysunki bez uprawnienia w OGÓLE NIE POWSTAJĄ, więc nie ma ich nawet w źródle, - automatyczna dokumentacja API wyłączona. /docs, /redoc i /openapi.json wypisują komplet tras, czyli spis wszystkich funkcji programu — ochrona zakładek nic by nie dała, gdyby obok leżał ich katalog. Znalezione TESTEM przechodzącym po trasach aplikacji, nie przeglądem kodu. KONTO ADMINISTRACYJNE zostaje w APP_USER/APP_PASSWORD, jak było. Nie leży w pliku kont, więc nie da się go skasować ani ograniczyć z ekranu. Konto założone w pliku o tym samym loginie NIE przesłoni administracyjnego — kolejność sprawdzania jest odwrotna, inaczej dałoby się odebrać uprawnienia jedynemu, kto może je nadawać. Uprawnienia administracyjnego nie da się też nadać z formularza: odsiewamy je w normalise(), a nie w handlerze, więc żadne spreparowane żądanie tam nie sięgnie. GRANICA JEST W HANDLERZE, NIE W SZABLONIE. Ukrycie pola chroni przed przypadkiem, nie przed kimś, kto zna nazwy pól — _limit_options() ścina opcje po stronie serwera i test wysyła spreparowane żądanie, żeby to potwierdzić. MAPA TRASA→UPRAWNIENIE JEST JEDNA (features.ROUTES). Rozproszenie jej po dekoratorach kończy się trasą, o której ochronie ktoś zapomniał — a taka dziura jest niewidoczna, dopóki ktoś jej nie znajdzie. Trasa bez wpisu wymaga administratora: przeoczenie ma ZAMYKAĆ, nie otwierać. Test idzie po trasach APLIKACJI, nie po wpisach mapy — inaczej potwierdzałby tylko sam siebie. Konta w pliku JSON na własnym podkatalogu NFS (nie tam, gdzie bazy — zamontowanie całego udziału obeszłoby bokiem DAN-25). Hasła wyłącznie jako hash scrypt, tym samym mechanizmem co APP_USERS. Zapis atomowy, bo przerwanie zapisu na NFS obcięłoby plik, czyli skasowało wszystkie konta naraz. Przy okazji przepisane trzy testy, które greppowały nawigację i main.py: menu powstaje teraz z katalogu funkcji, więc szukanie sztywnych linków w base.html niczego już nie sprawdzało. Wymaga wolumenu na konta — osobny PR w repo deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a0d1135db1 |
feat(prezentacja): cache-busting plików statycznych (PRE-26)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m36s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 11s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
build / build (push) Successful in 50s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m51s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m29s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 11s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Po deployu przeglądarka trzymała stare styles.css / *.js — ten sam URL, więc serwowała z cache mimo nowej wersji. Doklejamy do URL-a krótki HASH TREŚCI pliku: zmienił się plik → zmienił się URL → przeglądarka pobiera nowy; bez zmian URL zostaje ten sam i cache dalej działa (bustujemy tylko to, co się zmieniło). - `static_url(name)` + globalny helper Jinja `static()`: `/static/x?v=<md5[:8]>`. Hash liczony raz na proces (lru_cache) — nowy pod po deployu = świeży hash; brak pliku → `?v=0`, nie wywala strony. - Wszystkie odwołania w szablonach (styles.css, nasze *.js, vendor Leaflet) idą teraz przez helper zamiast surowego `/static/...`. Testy: +5 (hash w URL, zależny od treści, brak-pliku-bezpieczny, żaden szablon nie serwuje surowej ścieżki, base używa helpera). Test kolejności skryptów zaktualizowany pod nowy format. Prezentacja 214. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8cc329ab17 |
feat(prezentacja): zapamiętane predykcje okresowe (PRE-22) + wymaganie o cache-bustingu
build / build (push) Successful in 1m4s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 12m9s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 38s
Testy / Kontrola składni wszystkich warstw (push) Successful in 20s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m42s
Piata wskazowka partnerow: Kalendarz ma pozwalac liczyc predykcje dla KILKU okresow i je zachowywac, zeby wszystkie trafily potem do raportu („Skompiluj", PRE-23/24). Nowy predictions.js — magazyn w localStorage, tak jak wspolne dane formularza (PRE-21): serwer zostaje bezstanowy, zadne dane urodzeniowe ani tresci z baz nie laduja po stronie uslugi. Zakladka „Skompiluj" odczyta to samo miejsce przez window.astrololoPredictions (jedno zrodlo prawdy). Decyzje projektowe: - KLUCZEM TOZSAMOSCI JEST OKRES. Ponowne policzenie tego samego zakresu podmienia wpis zamiast dokladac duplikat — inaczej lista puchlaby przy kazdej probie z innym modelem albo budzetem. Dzieki temu zapis jest idempotentny, wiec dziala tez wariant bez strumienia (zapis przy wczytaniu strony z gotowym wynikiem) i odswiezenie niczego nie mnozy. - progress.js OGLASZA gotowy horoskop zdarzeniem `astrololo:horoscope` z profilem, zamiast sam zapisywac. To okno postepu, a nie magazyn — zapisywanie zostaje odpowiedzialnoscia predictions.js. Filtr po profilu pilnuje, zeby interpretacja natalna nie trafila na liste predykcji okresowych. - Przepelniony magazyn (horoskopy bywaja dlugie) jest ZGLASZANY uzytkownikowi, a nie polykany — inaczej wynik znikalby po cichu. UI: lista zapamietanych predykcji na Kalendarzu — okres, data zapisu, objetosc i przycisk usuwania. Skrypt podpiety w timeline.html (nie w base.html), zeby nie kolidowac z rownolegle otwartym #29. PRE-26 — dopisane wymaganie o wersjonowaniu plikow statycznych. Przy PRE-25 przegladarka podala STARY styles.css i powiekszanie kosmogramu „nie dzialalo", mimo ze klasa byla nakladana. Objaw jest zdradliwy: szablony sa nowe, wiec strona wyglada na zaktualizowana, a funkcja po prostu milczy. Po wdrozeniu moze to spotkac uzytkownikow. Weryfikacja na zywej aplikacji: dwa okresy zapisane; powtorzenie tego samego zakresu podmienilo wpis (dalej 2, tekst zaktualizowany); lista posortowana po dacie; predykcje przetrwaly przejscie na inna zakladke i powrot; interpretacja natalna NIE wpadla na liste; usuwanie zmniejszylo licznik 2 -> 1 i przerysowalo liste. Testy: 14 nowych. Calosc: prezentacja 101 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f34016a4a5 |
feat(prezentacja): wspolne dane formularza miedzy zakladkami + imie i nazwisko (PRE-21)
build / build (push) Successful in 58s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m4s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 38s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
Trzecia wskazowka partnerow. Kazda zakladke (Horoskop / Interpretacje / Kalendarz) wypelnialo sie od nowa — te same imie, data, godzina, strefa i miejsce. Teraz raz wpisane dane wedruja za uzytkownikiem, a zmiana w jednej zakladce przenosi sie na pozostale. Nowy formsync.js trzyma stan w localStorage. Dlaczego nie sesja na serwerze: serwer zostaje BEZSTANOWY — zadnego magazynu sesji, zadnych danych urodzeniowych trzymanych po stronie uslugi (spojne z postawa z LOG-32/PRE-16). Przy okazji dane synchronizuja sie tez miedzy osobnymi kartami przegladarki, bo zdarzenie `storage` daje to za darmo. Dolozone pole „imie i nazwisko" (wszystkie trzy zakladki) — potrzebne do naglowka raportu PDF (PRE-24). Handlery przyjmuja je i oddaja, wiec nie znika po przeliczeniu. Dwie rzeczy, ktore trzeba bylo domknac, zeby to dzialalo naprawde: 1. KOLEJNOSC SKRYPTOW. formsync.js ladowany w <head> z `defer` — skrypty defer wykonuja sie w kolejnosci dokumentu, wiec ten zdazy odtworzyc wspolrzedne, ZANIM geo.js zbuduje mape. Mapa startuje od razu we wlasciwym miejscu, zamiast przeskakiwac po chwili. 2. geo.js ustawia pola z KODU (`.value = ...`), co samo z siebie NIE wywoluje zdarzen — bez tego synchronizacja przegapilaby kazdy wybor z mapy, z wyszukiwarki i z „Tu i teraz". Dolozony setVal(), ktory jawnie zglasza `change`. Zakladka bez danego pola (np. Sygnifikatory) nie kasuje wartosci zapamietanej gdzie indziej; uszkodzony wpis w localStorage nie blokuje formularza. Q-14 rozstrzygniete: lancuch LaTeX->PDF stanie jako OSOBNA USLUGA render — spojne z izolacja swisseph (LOG-27), obraz produktu zostaje maly. Weryfikacja na zywym stacku (data+logika+prezentacja) w przegladarce: dane wpisane w Horoskopie pojawily sie w Interpretacjach; zmiana godziny w Interpretacjach dotarla do Kalendarza; pola nieobecne na zakladce (zodiak, system domow) przetrwaly; po POST imie zostalo, a horoskop policzyl sie normalnie. Testy: 22 nowe strukturalne. Calosc: prezentacja 67 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5c82e8bd9f |
fix(prezentacja): offset wzgledem GMT + nazwa lokalizacji po „Tu i teraz”
Dwie pierwsze wskazowki od partnerow biznesowych; pozostale piec zapisane jako wymagania (PRE-21..PRE-24) do zrobienia w kolejnych krokach. PRE-19 — offset wzgledem GMT. Etykieta mowila „Strefa (offset h)”, czyli nie bylo jasne, wzgledem czego liczymy przesuniecie. Teraz „Offset wzgledem GMT (h)” z podpowiedzia. Krok juz byl 15-minutowy (0,25 h) — dolozony zakres −12…+14, zeby nie dalo sie wpisac strefy, ktora nie istnieje. Zmiana w trzech zakladkach, ktore maja to pole (Horoskop, Interpretacje, Kalendarz). PRE-20 — po „Tu i teraz” wspolrzedne i pineska skakaly na biezace polozenie, ale w polu tekstowym zostawala STARA, wczesniej wpisana nazwa. Formularz pokazywal jedno miejsce, a liczyl dla innego — cicha pomylka, nic sie nie wywalalo. Handler zdarzenia astrololo:coords odswieza teraz nazwe przez reverseName. Przy okazji druga strona tego samego bledu: reverseName czysci pole ZANIM wysle zapytanie. Gdyby /reverse nie odpowiedzialo (brak sieci), zostalaby stara nazwa — lepiej puste pole i poprawne wspolrzedne niz nazwa, ktora klamie. Wymagania: PRE-19/20 (zrobione), PRE-21 wspolne dane miedzy zakladkami wraz z polem imie i nazwisko, PRE-22 wiele predykcji okresowych w pamieci sesji, PRE-23 zakladka „Skompiluj”, PRE-24 raport PDF przez LaTeX. Dolozone pytanie otwarte Q-14 o lancuch LaTeX→PDF (gdzie postawic TeX Live, silnik unicode owy pod glify, konwersja SVG) — decyzja wplywa na deploy i rozmiar obrazow. Testy: 12 nowych, strukturalnych na zrodle (JS-a nie uruchomimy, a obie regresje sa ciche). Sprawdzone sabotazem — po cofnieciu kazdej poprawki czerwienieja. Calosc: prezentacja 45 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
114b7eebdf |
feat(ui): okno postepu z logiem podczas pisania horoskopu
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 11m21s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m50s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 34s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 21s
build / build (push) Successful in 1m49s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m20s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m0s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 37s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
Generowanie trwa minutami, a zwykly POST nie dawal zadnego sygnalu — aplikacja wygladala na zawieszona. Teraz w trakcie pracy pojawia sie okno z logiem, zegarem i spinnerem. Log pokazuje RZECZYWISTE zdarzenia z serwera, nie udawany pasek postepu: - app/progress.py — strumien NDJSON; praca leci w watku roboczym, generator odpompowuje kolejke, wiec zdarzenia docz w TRAKCIE pracy, nie na koncu; heartbeat co 10s, zeby proxy nie uznalo polaczenia za martwe, - providers.generate(..., on_event) — raportuje kazda ture (start, czas trwania, liczba znakow, czy urwana), bo to tura trwa, - POST /chart/horoscope/stream w logice + proxy /horoscope/stream w prezentacji. Wynik: ostatnie zdarzenie niesie GOTOWY HTML wyrenderowany z tego samego szablonu, ktory renderuje przeladowanie strony (_prompt_result.html wydzielony z _prompt_block.html). Jedno zrodlo prawdy dla wygladu wyniku — okno wstawia go bez przeladowania. Degradacja: bez strumieniowania w przegladarce formularz idzie klasycznie i wszystko dziala jak wczesniej, tylko bez okna. Blad polaczenia konczy sie komunikatem w logu, nie cisza. BLAD ZNALEZIONY PRZY TESCIE NA ZYWO: petla kontynuacji odejmowala od budzetu ZAMOWIONY limit tury zamiast tokenow faktycznie wyprodukowanych — pierwsza tura zjadala caly budzet, wiec urwana odpowiedz nigdy nie doczekala sie dokonczenia i wracala do uzytkownika jako calosc. Naprawione i pokryte testem regresyjnym. Testy: 176 passed / 1 skipped (logika) + 17 (prezentacja). Zweryfikowane na zywo z wolna atrapa modelu: zdarzenia z poprawnymi czasami, okno z 11 liniami logu, wynik wstawiony bez przeladowania strony. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
163ace4283 |
feat(ui): interaktywny wybor modelu u dostawcy
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 11m14s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 10m19s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 46s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 27s
build / build (push) Successful in 1m18s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m2s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 36s
Testy / Kontrola składni wszystkich warstw (push) Successful in 23s
Uzytkownik wybiera nie tylko dostawce, ale konkretny model — Fable czy Opus u Anthropica, gpt-4o-mini czy gpt-5 u OpenAI, cokolwiek ma pobrane lokalnie. - app/llm/catalog.py: podpowiedzi modeli per dostawca wraz z oknem kontekstu, nadpisywalne przez <DOSTAWCA>_MODELS; GET /llm/models wystawia je dla UI. - Pole modelu w UI jest TEKSTOWE z datalista, nie zamknietym <select> — konto moze miec dostep do modeli, o ktorych kod nie wie, a nowe wychodza szybciej, niz aktualizuje sie katalog. Puste pole = model domyslny dostawcy. - static/models.js: zmiana dostawcy przelacza podpowiedzi, podmienia placeholder na model domyslny i pokazuje okno kontekstu wybranego modelu. - build_provider(name, model) — model z zadania wygrywa nad konfiguracja. WAZNE (znalezione przy tescie e2e): liczenie budzetu „maksymalny kontekst modelu" szlo przez build_provider(), ktory WYMAGA klucza API — bez klucza budzet cicho spadal do wartosci zapasowej i byl identyczny dla wszystkich modeli Anthropic. Budzet zalezy wylacznie od okna kontekstu, wiec doszlo resolve_model(), ktore rozwiazuje nazwe modelu bez budowania dostawcy. Teraz budzet realnie sie rozni: Opus/Fable 3,48 mln znakow, Haiku 536 tys., gpt-4o-mini 438 tys., llama3.1 8 tys. Pewnosc danych w katalogu: modele Anthropic pochodza z oficjalnej dokumentacji API (okna i limity zgodne z limits.py); modele OpenAI to podpowiedzi, ktorych nie weryfikowalem; lokalne zaleza od tego, co masz pobrane. Testy: 174 passed / 1 skipped (logika) + 17 (prezentacja). Nowy test strukturalny pilnuje, ze KAZDE wywolanie w dol niesie wybrany model i dostawce — dokladnie ta klasa bledu zlapala brakujacy parametr przy horoskopie okresowym. Zweryfikowane w przegladarce: przelaczanie dostawcy podmienia liste modeli. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4ef90b30bc |
feat(logic): dostawcy LLM i pisanie horoskopu (LOG-31)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m38s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m43s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 24s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 24s
build / build (push) Successful in 59s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m47s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 29s
Testy / Kontrola składni wszystkich warstw (push) Successful in 18s
Domyslnie model LOKALNY — prompt niesie oryginalne opisy z baz, wiec domyslnie NIC nie opuszcza sieci. Chmura wlaczana swiadomie (LOG-32). - app/llm/: LLMProvider (jak EphemerisEngine z LOG-24) + dwie implementacje. Lokalny serwer modelu (Ollama/vLLM/llama.cpp) i OpenAI mowia TYM SAMYM protokolem /chat/completions, wiec obsluguje je jedna klasa; Anthropic ma wlasny /v1/messages. Napisane na samym httpx — bez SDK openai/anthropic: mniej zaleznosci i pelna kontrola nad tym, co wychodzi z sieci. - Kazdy dostawca deklaruje `leaves_lan` — interfejs MUSI jawnie mowic, czy tresc baz opuszcza siec; UI na tej podstawie ostrzega. - Ponawianie z backoffem (429/5xx), timeouty, czytelne bledy zamiast stacktrace. - Klucz wylacznie z LLM_API_KEY (sekret), nigdy w repo ani w UI. - POST /chart/horoscope + GET /llm/health. WAZNE: prompt jest zwracany ZAWSZE — takze gdy model padnie lub brakuje klucza. Dzieki temu awaria dostawcy nie blokuje pracy: prompt mozna skopiowac i uzyc recznie. Prezentacja: wybor modelu (lokalny/Anthropic/OpenAI), przycisk „Napisz horoskop (AI)", wynik z informacja kto go napisal, czy dane opuscily siec, ile wskazan weszlo, oraz zastrzezenie ze to nie porada medyczna (PRE-15). Testy: 13 nowych (transport podstawiony — zaden prawdziwy model nie wolany), calosc 131 passed / 1 skipped. Zweryfikowane e2e na atrapie serwera modelu: horoskop napisany, leaves_lan=false, tokeny zliczone; a przy padnietym modelu / braku klucza / zlym dostawcy — czytelny blad i zachowany prompt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
04b26afa6d |
feat(logic): generator promptow do LLM + budzetowanie (LOG-29, LOG-30)
Pierwszy krok ustalonej kolejnosci: prompt z podgladem, uzyteczny od razu BEZ integracji API (ta przyjdzie w LOG-31). LOG-29 — app/prompt.py: - profil natal (ekran Interpretacje) i period (ekran Kalendarz), - prompt po polsku: zadanie -> dane horoskopu (pozycje, osie, Lots, aspekty, sekta, zodiak) -> wskazania z baz wg wagi -> instrukcje -> zastrzezenie, - twarde reguly: kazda teza musi cytowac konkretny sygnifikator, zakaz wychodzenia poza dostarczone dane, jawne wskazanie sprzecznosci, - deterministyczny: ten sam horoskop + budzet = ten sam prompt. LOG-30 — redukcja do budzetu (concise/medium/extensive): dedup -> grupowanie z licznikiem -> sortowanie wg punktacji sily (LOG-21) -> obciecie ogona (jednostka = CALE wskazanie) -> skracanie dlugich opisow. Statystyki zwracaja ile weszlo/pominieto i jaki byl prog — takze w tresci promptu, zeby model wiedzial, ze widzi wybor. POST /chart/prompt — zwraca sam prompt + statystyki, bez wolania modelu. Prezentacja: przycisk „Generuj prompt (AI)" na obu ekranach, wybor budzetu, pole z promptem + kopiowanie (z fallbackiem dla http bez secure context). WAZNE (znalezione przy tescie e2e): padnieta warstwa danych zabiera tylko wskazania — horoskop i OS CZASU zostaja, bo sa czysto obliczeniowe. Wczesniej blad bazy gubil cala osie czasu, czyniac prognoze okresowa bezuzyteczna. Zabezpieczone testem. Testy: 19 nowych, calosc 118 passed / 1 skipped. Zweryfikowane e2e w przegladarce: profil natal (3340 znakow) i period (15 zdarzen, 4728 znakow). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a2aabd37a5 |
feat(presentation): wyszukiwarka lokalizacji + mapa (OSM/Leaflet)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m39s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 28s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 21s
build / build (push) Successful in 57s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m47s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 29s
Testy / Kontrola składni wszystkich warstw (push) Successful in 22s
QoL 2/2: pole „Szukaj miejsca" (nazwa/adres/POI) + interaktywna mapa na formularzach horoskopu / interpretacji / kalendarza. Bez klucza API. - geocode.py: proxy OSM/Nominatim PO STRONIE SERWERA (poprawny User-Agent, throttling ~1 req/s, cache TTL 1h) — endpointy /geocode i /reverse. Wolanie z serwera, nie z przegladarki: latwiej trzymac polityke Nominatim i dziala niezaleznie od secure-context (http://<ip>). - _location_picker.html: wspolny partial (search + wyniki + mapa + atrybucja), wpiety includem do 3 formularzy z polami lat/lon. - geo.js: Leaflet — wyszukiwanie (debounce), klik na wynik ustawia lat/lon i centruje mape, klik/drag pineski ustawia wspolrzedne + /reverse pokazuje nazwe; sync z „Tu i teraz" (now.js emituje event astrololo:coords). - Leaflet 1.9.4 vendorowany lokalnie (static/vendor/leaflet, BSD-2-Clause, permisywny) — niezaleznosc od CDN; kafelki mapy z OSM. Marker jako divIcon (bez plikow PNG). - styles.css: style pod ciemny motyw. Zweryfikowane w przegladarce: domyslny widok (pineska na Szpitalu Barlickiego), wyszukanie „Wawel Krakow" -> lista -> klik ustawia 50.0547/19.9361 i przesuwa mape, klik w mape ustawia wspolrzedne + reverse wypelnia nazwe. Zero bledow w konsoli. /geocode i /reverse zwracaja szpital; cache dziala. Uwaga wdrozeniowa: pod prezentacji potrzebuje egressu do nominatim.openstreetmap.org; przegladarki — do tile.openstreetmap.org. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
de5d58958c |
feat(presentation): domyslna lokalizacja = Szpital Barlickiego, Lodz
QoL: formularze (horoskop / interpretacje / kalendarz) maja wstepnie wpisana lokalizacje urodzenia wlasciciela — Szpital Barlickiego w Lodzi (51.7739N, 19.4829E; potwierdzone reverse-geokodowaniem OSM: Kopcinskiego 22/28). Nie trzeba jej wpisywac za kazdym razem. - config.py: jedno zrodlo prawdy (DEFAULT_LAT/LON/LABEL, nadpisywalne ENV) + helper default_form(). - main.py: GET wstrzykuje default_form() + location_label do 3 formularzy z polami lokalizacji (significators pominiete — nie ma tam lat/lon). - szablony: dyskretna podpowiedz z nazwa lokalizacji, widoczna tylko na czystym formularzu (po POST znika, wygrywa wpisana wartosc). - „Tu i teraz" nadal nadpisuje domyslne wspolrzedne geolokalizacja. Zweryfikowane TestClientem: 3 strony renderuja 51.7739/19.4829 + etykiete; POST z innymi wspolrzednymi je zachowuje i chowa podpowiedz. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
c4a181b810 |
Spięcie osi czasu z bazą interpretacji i widokiem (LOG-14, 1B->2B)
Realizuje przepływ 1B->2B z notes2: predykcyjne sygnifikatory (z datami)
dopasowane do interpretacji z bazy.
Logika:
- timeline.py: zdarzenia niosą strukturę (directed/aspect/target dla dyrekcji,
lord/sign dla profekcji) do budowy tokenów.
- significators.interpret_events + _event_tokens: z każdego zdarzenia buduje
tokeny bazy (dyrekcja: [planeta][aspekt][cel]; profekcja: [władca][znak]) i
dopina interpretacje reużywając _facet_samples (AND tokenów, dedup, rozwinięcie).
- /chart/timeline: flaga interpret=true.
Prezentacja:
- nowa strona /timeline "Kalendarz": formularz (urodzenie + zakres dat) -> oś
czasu z technikami, datami i interpretacjami; nawigacja + wspólne now.js.
Walidacja E2E na realnym main_base.xlsx (2025-2026):
- dyr. Saturn kwadratura MC -> 50 interpret. ("...5th house" -> "abortion/miscarriage")
- Władca Roku Saturn (wiek 42) -> 63; strona renderuje badge dat/technik.
71 testów przechodzi (nowy test_timeline_interpret).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|