Przy każdej regeneracji gen_pages.py buduje archiwum do wrzucenia w okno
czatu: wszystkie pliki .md bundle'a z zachowaniem struktury katalogów
(28 plików: koncepty, indexy podkatalogów, log.md) plus wygenerowany
AI-README.md. Nie wchodzą pages/, zdjęcia, generatory ani __pycache__ —
paczka niesie samą wiedzę, nie rusztowanie.
Deterministycznie: stały czas modyfikacji wpisów (1980-01-01), stała
kolejność i stały poziom kompresji, więc archiwum jest bajt w bajt takie
samo, dopóki nie zmieni się treść — git nie widzi diffów z niczego
(zweryfikowane: dwa uruchomienia, ten sam md5).
Z tego samego powodu AI-README.md podaje jako datę nie odczyt zegara,
tylko najnowsze `ostatnia_aktualizacja` w bundle'u ("data treści") —
data z zegara robiłaby nową wersję pliku każdego dnia, także wtedy, gdy
nic nie doszło.
AI-README.md (PL + EN, generowany z bundle'a): czym jest baza, format
OKF (markdown + YAML frontmatter, jeden plik = jeden koncept), polecenie
odpowiadania wyłącznie z tych plików i objaśnienie dwóch pól niosących
pewność danych — `status_ceny` i `dostepnosc_6_13_02_2027`, z ostrzeżeniem,
żeby nie zgadywać dostępności przy 🔍 i nie podawać szacunków jako cen
potwierdzonych.
Link "Pobierz bazę (.zip) — wrzuć do ChatGPT/Claude'a i pytaj" jako piąty
kafel na start.html i pozycja w sekcji "Wejście" na linki.html.
Plus wpis do log.md podsumowujący całą zmianę terminu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
porownanie.html:
- nowa kolumna „Dostępność 6–13.02” z pola `dostepnosc_6_13_02_2027`,
skrócona do emoji + werdyktu (✅ DOSTĘPNE / 🔍 do sprawdzenia /
❌ BRAK (zweryf.)), pełna notatka w atrybucie title
- sortowanie kolumny układa decyzyjnie (wolne → do sprawdzenia → brak),
nie alfabetycznie po emoji
- wiersz Glemm Lodge na czerwono i z przekreśloną nazwą: zweryfikowany
brak miejsc bije kolor statusu ceny (reguła CSS niżej wygrywa)
- karty `priorytet: ARCHIWALNA` wypadają z tabeli i z bilansu do osobnej
sekcji „Archiwum” na dole strony
- legenda i bilans statusów pod tabelą
Termin wyjazdu wyjechał do stałych TERM_LABEL/TERM_SHORT/TERM_NOTE:
nagłówki wszystkich stron (BUNDLE_NAME, też w gen_viz.py), lead
start.html, opis kafla porównania i raport braków biorą go stąd.
Zmiana terminu = zmiana stałych i regeneracja.
Raport braków czyta teraz dostępność z pola frontmattera zamiast zgadywać
z nagłówków w body, i pomija karty archiwalne. Wypis po regeneracji
podaje bilans ✅/🔍/❌.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dom-dla-grupy i alpenparks-rehrenberg zostają w bazie jako karty
ARCHIWALNE: riscercz 08.2026 objął tylko obiekty z paczki, więc ich ceny
i dostępność opisują poprzedni termin (16–23.01) i dziś wprowadzałyby
w błąd. Oznaczone `priorytet: ARCHIWALNA (poza paczką 08.2026)` +
`archiwalna_od`, z ostrzeżeniem na górze body. Wypadają z tabeli
porównawczej i ze statusów dostępności (osobna sekcja Archiwum).
wyjazd.md przepisany od zera na nowy termin:
- tabela porównawcza czterech regionów z nowymi budżetami
- tabela dostępności 10 ofert z legendą ✅/❌/🔍 i bilansem
(1 zweryfikowany status, i to negatywny — 9 do weryfikacji)
- nowe wnioski przekrojowe (Kronplatz najlepiej znosi termin,
Saalbach traci przewagę cenową, Ischgl konkurencyjny z Kappl,
SFL bez styczniowej przewagi)
- nowa sekcja "Plan B na wypadek pełnego tygodnia" (13–20.02
i 27.02–06.03 na podstawie zweryfikowanego kalendarza Glemm Lodge)
Usunięte jako nieaktualne: wniosek o przewadze cenowej SFL (styczeń
= ich niski sezon), ranking ofert wyceniony po stawkach styczniowych,
rekomendacja terminu 16–23.01 i alternatywy 9–16.01.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nałożenie paczki ../ski-2027-kb-full na bundle: nowe body i pola
merytoryczne frontmatterów dla 4 regionów, 10 kart noclegów, szablonu
maila i indexów. Normalizacja OKF zachowana — title/description/
timestamp/tags zaktualizowane do nowej treści (timestamp 2026-08-01
z paczki), linki bundle-relative od "/".
Merytorycznie:
- termin 6–13.02.2027 to szczyt sezonu (Fasching, Popielec 10.02,
ferie bawarskie) — wszystkie budżety noclegowe i skipassowe w górę
- nowe pole `dostepnosc_6_13_02_2027` w każdej karcie noclegu,
legenda ✅ zweryfikowane dostępne / ❌ zweryfikowany brak /
🔍 brak danych do weryfikacji
- Glemm Lodge: ZWERYFIKOWANY brak (3/3 apartamenty zajęte), wolne
13–20.02 i 27.02–06.03 — jedyny obiekt z publicznym kalendarzem
- SFL traci styczniową przewagę cenową (luty = ich wysoki sezon),
Kronplatz najlepiej znosi termin, Ischgl konkurencyjny z Kappl
Pola merytoryczne z paczki nadpisują dotychczasowe; zachowane zostały
fakty, których paczka nie powtarza (adres Glemm Lodge, koszt_rodzina_*
dla Glemm Lodge zgodne z kosztorysem w jego body).
index.md root: bez zmian formalnych (§6/§11 — tylko okf_version),
listing przebudowany na symetryczne cztery regiony.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cztery regiony w jednym schemacie <region>/region.md + <region>/oferty/*.md.
Saalbach przestaje być wyjątkiem w korzeniu bundle'a.
- git mv region.md → saalbach/region.md, oferty/ → saalbach/oferty/
- linki bundle-relative przepisane: /region.md → /saalbach/region.md,
/oferty/* → /saalbach/oferty/*
- nowy saalbach/index.md wg §6, symetryczny do ischgl/kronplatz/sfl
Zdjęcia obiektów przetrwały bez zmian: mapowanie oferta→zdjęcia jest
kluczowane po polu `id` we frontmatterze (pages/img/<id>/), nie po
ścieżce pliku — gen_images.py:121,390 i gen_pages.py:695-709.
Region "Saalbach" w gen_pages.py bierze się z dirname konceptu, więc
przejście "" → "saalbach" obsługuje się samo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
W _page_shell dopisek idzie do istniejącej stopki, tym samym małym
wyciszonym fontem co nota o generatorze. Viewer grafu stopki nie ma (main
wypełnia okno), więc w gen_viz.py dopisek ląduje w nagłówku obok "OKF
bundle", w tej samej klasie .muted — przez łatkę na _load_template, tak
samo jak dotychczasowa łatka na viz.js: repo upstream zostaje nietknięte,
a brak kotwicy przerywa regenerację zamiast po cichu gubić dopisek.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kafel do /pages/linki.html był już czwarty w rzędzie, ale pod etykietą
"Linki do wysłania" — nazwa z ustaleń to "Wszystkie linki". Przy czterech
kaflach siatka auto-fit łamała się 3+1 i czwarty zostawał sam w wierszu;
minimum kolumny podniesione z 13rem do 16rem układa je 2×2, a na telefonie
i tak jest jedna kolumna.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nowa strona z pełnymi URL-ami wszystkich 24 stron na https://narty27.kapala.org
(BASE_URL — jedyne miejsce z domeną w kodzie), w sekcjach: Wejście, Regiony,
Oferty per region z priorytetem PLAN A/B z frontmattera, Pozostałe. Każda
pozycja to klikalny link, widoczny adres tekstem do skopiowania i krótki opis.
Lista powstaje z tych samych danych co porownanie.html i start.html —
regiony z wyjazd.md, oferty z katalogów i frontmatterów, "Pozostałe" to
koncepty, których nie złapała żadna wcześniejsza sekcja. Nowa oferta wpada
na listę sama przy regeneracji.
Przyciski "Kopiuj wszystkie" (plain text: nazwa sekcji + adresy) i "Kopiuj
link startowy"; navigator.clipboard z odwrotem na zaznaczenie, bo po http
z adresu IP nie ma bezpiecznego kontekstu. Do nawigacji każdej strony
i na start.html doszła pozycja "Linki".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wejściem jest teraz landing pages/start.html (trzy kafle: porównanie ofert,
analiza wariantów, graf + kafle czterech regionów ze zdjęciem najlepszej
oferty w tle), a graf schodzi do roli załącznika. start.html linkuje
root-absolutnie, bo ten sam plik jest serwowany jako /index.html i jako
/pages/start.html.
pages/porownanie.html: jedna tabela wszystkich 12 ofert prosto z
frontmatterów (miniatura, priorytet, region, ceny, status, odległość, www),
sortowanie klikiem w nagłówek (czysty JS, bez zależności), wiersze barwione
statusem ceny. Nad tabelą podsumowanie per region — budżet całkowity i jeden
wniosek parsowane z tabeli i „Wniosków przekrojowych” w wyjazd.md, bez
zaszywania nazw regionów w kodzie.
Karty ofert dostały galerię (duże zdjęcie + miniatury, lazy loading),
a nawigacja na każdej stronie prowadzi do startu i porównania.
Komenda deployu (kopiuje start.html jako /content/index.html) jest
wypisywana przez gen_pages.py. Pliki .md bez zmian, check_okf zielony.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Skrypt pobiera 3–5 zdjęć ze strony z pola `www` frontmattera: og:image,
potem galeria z HTML-a. Odsiew: nazwa pliku (logo/ikona/mapa), waga >30 KB,
proporcje, przezroczystość i płaskie tło (plakietki), odcisk percepcyjny
(duplikaty z srcset). Na stronie marki z wieloma obiektami skrypt schodzi
do podstrony obiektu i filtruje adresy po jego nazwie własnej — inaczej
oba AlpenParksy dostałyby zdjęcia cudzych resortów.
Mapowanie oferta→zdjęcia to konwencja katalogowa (pages/img/<id>/NN.jpg),
pliki .md nietknięte. Bez zdjęć: booking.com i corones.it (blokada bota),
apart-ideal-serfaus (404 pod adresem z frontmattera), dom-dla-grupy
(koncept bez `www`).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tańsze stawki 385/231 € to VIP Skipass: tylko dla nocujących
w Ischgl/Mathon, 46 urządzeń, NIE działa na wyciągach Kappl. Nasz
PLAN A to nocleg w Kappl, więc obowiązuje Silvretta Skipass
(451/257 €, 75 urządzeń z Kappl i See) — ceny w tabelach zostają bez
zmian, zmienia się ich uzasadnienie.
Przy okazji dwa błędy w karcie, które rozstrzygnięcie odsłoniło:
tabela 451/257 € była podpisana "VIP Skipass" (to Silvretta), a sekcja
"Teren" przypisywała VIP-owi zasięg całego Paznaun łącznie z Kappl —
czyli dokładnie to, czego VIP nie obejmuje.
Pułapka wiekowa 8–<17 potwierdzona i zostaje w wyjazd.md; dopisany
plus po stronie Silvretty: dzieci <8 lat gratis (przy dwójce takich
dzieci grupa schodzi z ~5 150 € do ~4 640 €). Nowy wiersz "Dzieci
gratis" w tabeli porównawczej — b.d. tam, gdzie bundle nie ma danych,
zamiast domyślnego "brak".
Źródło: ischgl.com, cennik 26/27 (26.11.2026–02.05.2027), sprawdzone
03.08.2026.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cztery sekcje skipassowe uzupełnione o stawki przysłane przez grupę,
każda z adnotacją źródła i datą. Istniejące tabele nietknięte —
w każdym regionie nowe liczby stoją obok starych, z datami obu.
Saalbach: peak season 440/330/220 € potwierdza dotychczasową prognozę
(i jest górną granicą, bo nasz termin jest poza szczytem).
Kronplatz: twardy cennik, ale sezonu 2025/26 — nie tego, na który
jedziemy; szacunki 26/27 zostają.
Ischgl: dane od grupy SPRZECZNE z cennikiem w karcie — 385/231 € wobec
451/257 € i próg młodzieży 10–17 wobec 8–<17. Obie wersje zestawione,
rozstrzygnięcie zostawione grupie; do budżetowania nadal wariant
droższy. Jeśli obowiązuje próg 10–17, znika "pułapka wiekowa", która
jest dziś głównym argumentem przeciw Ischgl w tabeli w wyjazd.md.
SFL: link informacyjny (portal agregujący, nie cennik oficjalny).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Karta zbiorcza pages/index.html grupowała tylko korzeń i oferty/ —
dziewięć konceptów wariantów dostawało własne karty, ale nie było ich
na liście. Grupowanie idzie teraz po katalogach najwyższego poziomu,
a nazwa grupy to pierwszy nagłówek index.md tego katalogu, więc kolejny
region wystarczy dodać do bundle'a, żeby pojawił się tu sam.
Linki wariantów w root index.md prowadzą do region.md, nie do index.md
podkatalogów: pliki zarezerwowane nie mają kart, więc w pages/ taki
odnośnik degenerował się do linku na samego siebie.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Frontmattery nowych konceptów dostają pola OKF (title/description/
timestamp/tags) przed polami rozszerzeń producenta, które zostają
nietknięte. Body plików bez zmian — skopiowane dokumenty nie miały
żadnych linków markdown, więc nie było czego przepisywać na formę
bundle-relative.
Nowe indexy (§8/§6): <region>/index.md i <region>/oferty/index.md —
bez frontmattera, listing z opisami z frontmatterów konceptów.
wyjazd.md dostaje tabelę noclegów w wariantach z linkami do wszystkich
sześciu kart — bez tego cztery nowe oferty byłyby w grafie sierotami
(indexy nie są konceptami, a region.md wariantów do ofert nie linkuje).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ischgl/Paznaun, Kronplatz i Serfaus-Fiss-Ladis wchodzą do bundle'a jako
podkatalogi <region>/region.md + <region>/oferty/*.md — ta sama konwencja,
co dotychczasowy (bazowy) Saalbach.
Root index.md źródłowej paczki nie jest kopiowany: jego treść rozdzielona
zgodnie z rolami dokumentów tego bundle'a — listing wariantów do sekcji
"Warianty alternatywne" w index.md, tabela porównawcza i wnioski
przekrojowe do wyjazd.md (tam, gdzie już mieszka ranking i decyzje).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Panel szczegółów renderuje pole `resource` jako klikalny odnośnik
(viz.js:171-183), więc ustawienie tam ścieżki pages/<id>.html daje
przejście z grafu do pełnej treści dokumentu — zero zmian w viewerze.
Ścieżka jest względna wobec viz.html, więc działa lokalnie i po deployu.
Węzły external zachowują swoje URL-e (podmiana leci przed dołożeniem
węzłów zewnętrznych). Jeśli koncept miałby własny `resource` we
frontmatterze, trafia do danych węzła jako `resource_frontmatter`.
Zmiana wyłącznie w window.BUNDLE — pliki .md nietknięte, check_okf.py
nadal zielony.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Graf pokazywał strukturę, ale treść (ceny, dostępność, plusy/minusy)
siedziała tylko w panelu szczegółów viewera. gen_pages.py renderuje każdy
koncept do pages/<id>.html: metadane frontmattera jako tabelka (wszystkie
pola, też polskie rozszerzenia), body poniżej, plus pages/index.html
z listą kart.
Renderer: python-markdown, jeśli jest w systemie; tutaj go nie ma, więc
działa fallback na samej bibliotece standardowej (nagłówki, akapity,
tabele GFM, listy z - [ ], cytaty, kod, hr, inline bold/kursywa/kod/linki).
Linki miedzy konceptami przepisane na względne <id>.html, linki
zewnętrzne bez zmian. Pliki zarezerwowane (index.md, log.md) nie są
konceptami (§3.1) i nie mają kart — linki do nich prowadzą do listy kart.
Pliki .md nietknięte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jedyna ingerencja w logikę viewera w tym repo, wydzielona do osobnego
commita zgodnie z ustaleniem.
Kolor węzła jest sterowany danymi ("background-color": "data(color)"),
więc odróżnienie kolorem załatwiła sama warstwa danych (poprzedni commit,
zero zmian w viewerze). Kształt już nie — cytoscape'owa tablica stylów
siedzi w viz.js. Dokładamy do niej DOKŁADNIE jedną regułę:
selector: 'node[type = "external"]'
shape: round-diamond, border-style: dotted, font-size: 10
To ta sama konwencja co istniejące reguły node[?stale] i
node[status = "deprecated"] — selektor po polu danych. Reguła jest
wstawiana przed node[?stale], czyli przed node:selected, więc
podświetlenie zaznaczenia nadal wygrywa.
Podmiana dzieje się w locie, na stringu wstawianym do viz.html; plik
viz.js w repo upstream pozostaje nietknięty. Jeśli upstream zmieni
kotwicę, gen_viz.py przerywa z błędem zamiast po cichu wygenerować
viewer bez reguły.
Zweryfikowane: viz.js w viz.html == upstream + ta jedna reguła (diff
bajtowy równy jej długości), viz.css nadal bajt w bajt, składnia
osadzonego JS-a sprawdzona node --check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gen_viz.py wyciąga adresy z konceptów i dokłada je do window.BUNDLE jako
węzły typu "external" (teal #0d9488, odróżnialne od szarych konceptów)
z krawędzią koncept → link. Cztery niezależne źródła, każde wyłączalne
stałą SOURCES: md-link, bare-url, bare-domain, frontmatter.
Deduplikacja po znormalizowanym URL-u: bez fragmentu #, bez trailing
slash, schemat i host małymi literami. Odfiltrowane domeny techniczne:
github.com/GoogleCloudPlatform, localhost, 192.168.*, 100.*.
Klik w węzeł external otwiera URL w nowej karcie bez zmiany w viewerze:
pełny adres trafia do pola `resource`, które istniejący panel szczegółów
renderuje jako <a target="_blank" rel="noopener"> (viz.js:171-183). To
mniej inwazyjny wariant niż podpinanie się pod cy.on("tap", "node").
Uwaga: body konceptów nie zawiera dziś żadnego adresu http(s):// —
wszystkie adresy obiektów siedzą we frontmatterze (www:, gdzie_szukac:),
w body są tylko gołe domeny. Samo body + http/https dałoby 0 węzłów,
stąd źródła bare-domain i frontmatter.
Logika viewera nietknięta — viz.js i viz.css nadal bajt w bajt z
upstreamu. Bundle bez zmian: 0 zmodyfikowanych plików .md,
check_okf.py nadal zielony.
Wynik: 9 konceptów + 12 external, 23 krawędzie (9 + 14).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
check_okf.py — tylko stdlib, minimalny parser YAML wystarczający dla
frontmatterów w tym bundle'u. Sprawdza trzy warunki z §9:
- każdy nie-zarezerwowany .md ma parsowalny blok frontmattera YAML,
- każdy frontmatter ma niepuste pole `type`,
- pliki zarezerwowane bez frontmattera, poza root index.md, w którym
dozwolone jest wyłącznie okf_version (§11).
Wynik na frontmatterach zweryfikowany krzyżowo z PyYAML; walidator
przetestowany na 5 wstrzykniętych naruszeniach (każde wykryte).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Viewer jest samowystarczalny — bundle jest wpiekany do HTML jako
window.BUNDLE (nodes/edges/bodies), nie ma odczytu .md w runtime, więc
nie potrzeba manifestu ani konkretnej ścieżki serwowania. Źródłem danych
jest gen_viz.py, uruchamiany po zmianach w bundle'u.
Logika viewera (viz.js, viz.css, szkielet templates/viz.html) wstawiona
dosłownie z upstreamu — zweryfikowane bajt w bajt. gen_viz.py koryguje
wyłącznie zbieranie danych, w dwóch miejscach niezgodnych ze spec:
- _extract_links pomijał linki bundle-relative "/..." (generator.py:74),
czyli formę zalecaną w §5.1 — bundle zgodny ze spec dawał graf z 0
krawędzi. Sam viewer te linki rozumie (viz.js:297).
- _walk_concepts pomijał tylko index.md, więc zarezerwowany log.md
trafiał do grafu jako koncept typu "Unknown" (wbrew §3.1).
Wynik: 9 konceptów, 9 krawędzi (było 9 i 0).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- index.md: frontmatter zredukowany do samego okf_version (§6/§11),
body przepisane na listing sekcji w formacie §6.
- Nowy koncept wyjazd.md (type: Trip) przejmuje metadane wyjazdu
(nazwa/grupa/termin) oraz ranking ofert i najbliższe kroki z index.md.
- Każdy koncept dostał title/description/timestamp/tags (§4.1);
polskie pola zostawione bez zmian jako rozszerzenia.
- Wszystkie linki wewnątrz bundle'a na bundle-relative z / (§5.1).
- Nowe: oferty/index.md (§6) i log.md (§7).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>