Releases: mtsle/ai-faq-generator
Release list
AI FAQ Generator 1.1.0
Wydanie po zamkniętym audycie wieloagentowym. Naprawione wszystkie znalezione błędy.
Co się zmieniło dla właściciela witryny
- Dziennik pytań przestał gubić najnowsze wpisy. Sprzątanie starych rekordów liczyło granicę po numerze wpisu, a nie po dacie, więc przy włączonej retencji potrafiło usunąć wpisy nowsze zamiast najstarszych. To jedyna naprawiona usterka powodująca nieodwracalną utratę danych.
- Aktualizacja bazy jest bezpieczniejsza. Numer wersji struktury rośnie dopiero po udanej migracji. Wcześniej przerwana migracja zostawiała wtyczkę w stanie, w którym uważała się za zaktualizowaną.
- Odinstalowanie sprząta całą sieć witryn. Na instalacji wielowitrynowej dane były usuwane tylko z tej witryny, w której akurat trwało odinstalowywanie.
- Uszczelnione bramki uprawnień, limity i obsługa błędów w wielu miejscach.
Dla informatyka
Runner testów: 62 zestawy, 0 niezaliczonych. Każda naprawa ma strażnika w testach oraz dowód, że po jej cofnięciu ten strażnik czerwienieje.
Opis audytu i pełna lista napraw: katalog audyt/.
Załącznik ai-faq-generator.zip to gotowa paczka instalacyjna (103 pliki), bez testów i dokumentacji źródłowej.
AI News Portal 1.1.0
Wydanie po zamkniętym audycie wieloagentowym. Naprawione wszystkie znalezione błędy.
Co się zmieniło dla właściciela witryny
- Wtyczka wie teraz, w jakiej wersji ma bazę. Nie miała tego wcale: po zmianie struktury tabel nie istniała żadna ścieżka aktualizacji. Wersja rośnie dopiero po przebudowie potwierdzonej odczytem z bazy.
- Limit długości materiału obowiązuje bezwzględnie. Wcześniej kilka ścieżek awaryjnych omijało sufit 20 000 znaków i do modelu trafiał materiał dowolnej wielkości.
- Odstęp między zapytaniami do tego samego serwera. Nie istniał w produkcie. Wtyczka przestaje uderzać w jedno źródło kilka razy pod rząd.
- Odinstalowanie sprząta całą sieć witryn, a nie tylko bieżącą.
- Lista artykułów przestała czytać dysk cztery razy na każdą kartę. Koszt rósł liniowo z liczbą kart; teraz pierwsza karta wykonuje trzy odczyty, a kolejne zero.
Dla informatyka
Runner testów: 29 zestawów, 2306 asercji, liczonych co do jednej. Każda naprawa ma strażnika w testach oraz dowód, że po jej cofnięciu ten strażnik czerwienieje.
Opis audytu i pełna lista napraw: katalog audyt/.
Załącznik ai-news-portal.zip to gotowa paczka instalacyjna (45 plików), bez testów i dokumentacji źródłowej.
AI News Portal 1.0.0 — Domknięcie
Krok 8 z 8 — ostatni. Wtyczka jest ukończona: audyt całości przeszedł, dokumentacja mówi to samo co kod, paczka dla klienta stoi na pulpicie.
Co weszło w Kroku 8 (Domknięcie)
Etapy 8.1–8.3: naprawa zamka zdejmowanego bezwarunkowo, wydzielenie ekranów kokpitu do traitu, RWA (kontrast 4,63:1, cele dotykowe 24 px) — oraz naprawa runnera testów, który od Kroku 7 stawiał ✓ przy zerze wykonanych asercji. Kryterium od tamtej pory: liczba asercji === N, nie kod wyjścia.
Etapy 8.4–8.6, 8.9: dokumentacja klienta w repo (5 PDF, 4 schematy z jednego opisu .drawio+.svg), README.md i readme.txt pilnowane testem zderzającym każdą liczbę z kodem, odchudzenie instrukcji 30 MB → 8,5 MB bez utraty treści, sufity wagi w audycie PDF.
Etapy 8.7–8.8: przepustowość (sufit materiału 12 000 znaków, własny budżet przycisku „Opublikuj teraz") i trzy warianty zdjęcia na kategorię.
Etap 8.10 — audyt końcowy. Przegląd całego kodu według dziewięciu osi, trzej niezależni krytycy-obalacze, 10/10 mutacji zabitych, odbiór na żywym WordPressie. Naprawy z audytu:
| co | dlaczego |
|---|---|
| Zapis Ustawień nie dubluje harmonogramu | wp_get_scheduled_event() oddaje zdarzenie najbliższe, więc pojedynczy przebieg „na już" przesłaniał harmonogram i każdy zapis dokładał kolejny tick — podwajając zużycie dobowej puli AI. Teraz sprawdzana jest cała tablica zaplanowanych zdarzeń |
| Pusta odpowiedź modelu kończy sprawę | Była ponawiana w nieskończoność: ta sama pozycja paliła slot na każdym przebiegu i blokowała kolejkę. Teraz: jedno ponowienie, po drugiej pustej próbie status „nieudany" |
| Budżet przebiegu 20 s → 25 s | Ten sam ładunek potrafił liczyć się 9,74 s albo 20,58 s (wariancja myślenia modelu) — poprawna odpowiedź przepalała slot. Odbiór na żywo potwierdził: publikacja zajęła 21,9 s |
| Dokumentacja uzgodniona z kodem | Tabela awarii mówiła „timeout → nieudany", a kod zostawia pozycję w kolejce; poprawione też uprawnienia ról i powody pominięcia |
Werdykt krytyka bezpieczeństwa: oś bezpieczeństwa i odinstalowanie czyste — próba obalenia się nie powiodła. Odinstalowanie zweryfikowane na żywym WordPressie: zero śladu w ośmiu kontrolach (opcje, transienty, tabela, wpisy, metadane, terminy, cron).
Stan końcowy
- Runner: 2083 asercje, 15 segmentów, 29 zestawów, komplet zielony.
- Audyt PDF: 20/20 stałych kodu zacytowanych zgodnie, sufity wagi dotrzymane.
- Paczka: ZIP z białej listy (45 plików) + 5 PDF instrukcji, wersja podstawiana z nagłówka wtyczki — dokumenty nie mogą skłamać numeru.
🤖 Generated with Claude Code
AI News Portal 0.7.0 — Zabezpieczenia wtyczki
Krok 7 z 8. Wtyczka wysyła własne nagłówki bezpieczeństwa — ale tylko tam, gdzie nikt ich jeszcze nie ustawił.
Nagłówki bezpieczeństwa
Do tej pory wtyczka nie wysyłała ani jednego nagłówka: wszystko, co widać było w odpowiedzi na poligonie, pochodziło z motywu. Na motywie bez własnej polityki — a to większość motywów — strony Centrum Wiedzy szły bez żadnej ochrony.
Nowa klasa Security na template_redirect z priorytetem 0. Obejmuje także kanał RSS i ramkę oEmbed, a kokpitu nie dotyka, bo ten hook w wp-admin nie chodzi. Trzy widoki traktowane osobno i świadomie:
| widok | co dostaje |
|---|---|
| strona | Referrer-Policy, X-Content-Type-Options, X-Frame-Options, Permissions-Policy, CSP frame-ancestors 'self' |
| embed | to samo bez X-Frame-Options i bez CSP — ramka oEmbed ma się dać osadzić |
| feed | tylko nosniff i Referrer-Policy — reszta dla XML-a nic nie znaczy |
Zasada: uzupełniamy, nigdy nie nadpisujemy. Nagłówek już obecny zostaje nietknięty, a wysyłka idzie z replace = false, bo nagłówków dokładanych przez serwer WWW headers_list() nie pokazuje.
Pełnego CSP wtyczka nie narzuca. Front renderuje motyw klienta: polityka bez 'unsafe-inline' zabija typowy motyw, a z nim daje bezpieczeństwo pozorne. Gdy motyw ma własne CSP z frame-ancestors, wtyczka milczy także o ramkach; gdy ma CSP bez frame-ancestors — zostawia X-Frame-Options i zatyka dziurę.
Dwie ścieżki wyłączenia: filtr ainp_security_headers oraz stała AINP_NO_SECURITY_HEADERS w wp-config.php.
Materiał z cudzej strony
Ogrodzenie promptu chroniło dotąd wyłącznie treść. Tytuł i adres źródła szły do modelu surowe i poza ogrodzeniem — czyli tam, gdzie widzi on nasze instrukcje. Wystarczyło opublikować w obserwowanym kanale wpis o tytule ze znacznikiem zamykającym. Teraz wszystkie trzy pola przechodzą przez czyszczenie nieczułe na wielkość liter, z sufitami długości.
Pierwsza rezerwacja slotu AI
Szła przez add_option(), a ta funkcja robi INSERT ... ON DUPLICATE KEY UPDATE — przy kolizji nadpisuje cudzy wiersz i mimo to zwraca prawdę. Dwa przebiegi startujące naraz zapisałyby oba „zużyto 1", a wywołania poszłyby dwa. Teraz INSERT IGNORE, tak jak zamek przebiegu od Kroku 5.
Fraza wyszukiwania
ainp_s szło wprost do zapytania bez górnej granicy, a WordPress robi z każdego słowa osobny warunek LIKE. Sufit 120 znaków, cięty po znakach, nie bajtach.
Zamknięte samym pomiarem, bez zmian w kodzie
Klucz API ma w bazie autoload = off. Opcja Ustawień nie zawiera promptu. Transport chodzi na wp_safe_remote_get we wszystkich trzech miejscach, więc rdzeń odrzuca loopback, sieć prywatną, 169.254 i porty spoza 80/443/8080.
Liczby
Runner 27/27 zestawów. Nowe zestawy Kroku 7: 70 asercji o nagłówkach, 22 o materiale wejściowym. Odbiór po HTTP na żywej instalacji: 31/31, dwa razy pod rząd, sześć ścieżek — archiwum, kategoria, artykuł, embed, feed, kokpit.
AI News Portal 0.6.0 — Centrum Wiedzy
Krok 6 z 7. Portal ma wreszcie front: artykuły, które wtyczka publikuje sama od Kroku 5, mają teraz własne, czytelne miejsce na stronie.
Co doszło
Archiwum /centrum-wiedzy/ — lista kart od najnowszych, po 10 na stronie, z paginacją numeryczną. Karta pokazuje zdjęcie kategorii, kategorię, tytuł, lead ucięty do dwóch linii i datę.
Zdjęcia kategorii jadą w paczce wtyczki — siedem plików w assets/kategorie/, po jednym na kategorię domyślną. Nic nie jest pobierane z sieci: zdjęcia ze źródłowych artykułów to cudze pliki bez zgody, a CSP motywu i tak blokowałby obce domeny. Gdy klient dopisze własną kategorię, karta rysuje kafelek z inicjałem.
Wyszukiwarka na własnym parametrze ainp_s — zwykłe ?s= porwałoby routing do search.php motywu, bo is_search() jest w hierarchii szablonów sprawdzane przed is_post_type_archive(). Przy ainp_s wynik zostaje na szablonie portalu, a adres pozostaje /centrum-wiedzy/?ainp_s=karma.
Kategorie jako prawdziwe linki do archiwów taksonomii, nie filtr JS. Pokazywane są wyłącznie te, w których coś jest.
Szablon artykułu — kolumna tekstu ok. 70 znaków, metryczka, ramka źródła z rel="nofollow noopener", powrót do archiwum.
Motyw ma pierwszeństwo. locate_template() pytany jest o nazwy szczegółowe (archive-ainp_article.php, taxonomy-ainp_topic.php, single-ainp_article.php), a fragment karty da się podmienić plikiem ai-news-portal/card.php w motywie. Arkusz wtyczki nie ustawia kroju pisma, tła ani koloru tekstu — wnosi układ i jeden akcent, żeby Centrum Wiedzy nie wyglądało jak obce ciało.
Zero JavaScriptu, jeden breakpoint. Na telefonie jedna kolumna, pole szukania na całą szerokość, cel dotyku 44 px.
Naprawione
Goły /centrum-wiedzy/kategoria/ zwracał 404 — dług z Kroku 1. Teraz 301 na archiwum.
Dowody
Runner: 25 zestawów, wszystkie zielone. Nowe zestawy Kroku 6: krok6-portal-test.php (31 asercji) i krok6-karty-test.php (90). Odbiór po HTTP na żywym dworek.local: 30/30, dwa razy pod rząd.
AI News Portal 0.5.0 — Krok 5: Automatyzacja
Portal pracuje sam. Zdarzenie ainp_tick chodzi co godzine i prowadzi trzy fazy — pobierz, przygotuj, opublikuj — pod jednym zamkiem, w budzecie czasu, ktory pilnuje, zeby przebieg nie zginal na limicie hostingu.
Co doszlo w Kroku 5
- Harmonogram
ainp_tick(hourly) zakladany przy aktywacji i domykany nainit, wiec aktualizacja wtyczki nie zostawia klienta bez automatu. - Przejecie pozycji przez baze:
new → processing → new, atomowo w klauzuliWHERE, z odzyskiem porzuconych po 15 minutach. - Budzet czasu dzielony na fazy, z timeoutem kazdego zadania przycinanym do tego, co zostalo. Ponizej 3 sekund zadanie nie startuje wcale — porazka po dwoch sekundach nie jest wina serwisu i nie ma prawa podbijac licznika prob.
- Reczne wznowienie nieudanych z panelu, z licznikiem kosztu: klient widzi nie tylko ile pozycji wroci do kolejki, ale ile z nich zajmie wywolanie z dobowej puli.
- Ekran Materialy pokazuje ostatni automatyczny przebieg jako stan, nie jako jednorazowy komunikat. Automat, ktoremu skonczyla sie pula, przestal byc nieodrozninalny od automatu, ktory nie dziala.
- Zamek przebiegu to opcja
ainp_run_lockzakladana przezINSERT IGNORE, z przejeciem po trupie przez CAS. Dawny transientainp_prepare_locknie istnieje.
Dowody
- runner 23/23 zestawow, asercje Kroku 5: 163
- seria mutacyjna kodu po naprawach poaudytowych: 44/44, proba kontrolna 0/23
- odbior na zywej witrynie: 48/48, dwa razy pod rzad, baza niezmieniona przed i po
Uwagi
Bez migracji bazy — Krok 5 nie zmienil schematu. Wtyczka 1 (AI FAQ Generator) pozostaje zamrozona na v1.0.0.
AI News Portal v0.4.0 — Krok 4: AI i publikacja
Krok 4 z 7 domkniety. Portal przestaje byc kolekcjonerem linkow: pozycja z kanalu RSS przechodzi teraz przez model i konczy jako opublikowany artykul z kategoria.
Co doszlo
Wywolanie modelu (Gemini) — atomowa rezerwacja slotu z dobowej puli PRZED zadaniem (timeout w polowie wywolania zuzywa pule po stronie Google tak samo jak sukces), timeout dobierany do pozostalego budzetu czasu, wymuszony schemat odpowiedzi z topic jako enum.
Bramka slow wymaganych przed modelem — jedyne miejsce, przez ktore przechodzi kazda droga do Gemini. Powod jest strukturalny: odcisk tresci dziala jak znacznik "juz przetworzona", wiec kazda zmiana listy przez klienta uniewaznia wiersze przygotowane wczesniej i zaden filtr wczesniejszy juz ich nie dosiegnie. Zmierzone na zywym materiale: 9 z 34 gotowych pozycji bylo poza tematem.
Kontrakt odpowiedzi (Validator) — title 5-140, lead 20-400, tresc min. 800 znakow TEKSTU (nie HTML-a), kategoria dokladnie z listy, porownywana bez ogonkow i wielkosci liter, ale zapisywana zawsze w pisowni z Ustawien.
Publikacja (Publisher) — jeden wpis, jedna kategoria przez wp_set_object_terms() po insercie, obowiazkowy link do zrodla, idempotencja pytajaca o post_status => 'any'. Nieudane przypisanie kategorii NIE kasuje wpisu, tylko przestawia go na szkic: tresc kosztowala wywolanie z puli. Artykul demo znika po pierwszej prawdziwej publikacji, ale tylko jesli nikt go nie edytowal.
Panel — pole na klucz API (osobna opcja, bez autoladowania, nigdy nie renderowana z powrotem), edytowalny prompt z czterema znacznikami, przycisk "Opublikuj teraz" z licznikiem "Wywolan AI dzis: N z M", i natychmiastowy pierwszy przebieg po zapisaniu Ustawien.
Dowody
- runner 19/19 zestawow, 406 nowych asercji w Kroku 4,
- test mutacyjny 49/49 przy zielonej probie kontrolnej na kopii calego katalogu,
- odbior na zywej instalacji 60/60, przy powtorce 57/57 — idempotentny.
Co odbior zlapal, a atrapy nie
Pierwszy prawdziwy przebieg skonczyl sie HTTP 400: Schema w Gemini nie jest pelnym JSON Schema i nieznane pole additionalProperties wywracalo cale zadanie. Naprawa poszla razem z asercja na biala liste pol schematu.
Tabela testow w planie: 20 → 22 (bramka wariantu C i bramka przed modelem).
AI News Portal v0.3.0 — Krok 3: Filtr i treść
Krok 3 z 7 domknięty. Wtyczka 2 potrafi teraz odsiać nietrafiony materiał bez jednego żądania sieciowego, dobrać pełną treść, gdy kanał podał samą zapowiedź, i rozpoznać ten sam tekst pod dwoma adresami.
Co przybyło
Filter (3.1) — słowa wykluczające z granicą słowa opisaną przez \p{L}, nie \b; remove_accents() po obu stronach porównania. Wejściem jest wyłącznie to, co przyszło w kanale, więc odrzucenie kosztuje zero żądań HTTP i zero wywołań AI.
Powód w tabeli (3.2) — pozycja odsiana trafia do bazy ze statusem skipped i czytelnym powodem w kolumnie note, widocznym na ekranie Materiały. Zapisujemy ją, zamiast pomijać: klucz UNIQUE na url_hash sprawia wtedy, że kanał nie podaje jej w kółko.
Article (3.3–3.5) — scraping, gdy treść z kanału ma mniej niż 1200 znaków gołego tekstu; ekstrakcja, która wycina to, co powtarza się na każdej podstronie, i wybiera blok z największą ilością tekstu w akapitach; wp_kses_post() przy zapisie; próg 500 znaków, poniżej którego pozycja idzie do skipped; budżet pamięci 80% limitu, sprawdzany przed żądaniem.
Odcisk treści (3.6) — Runner::prepare_item() prowadzi pozycję przez filtr, scraping, canonical i odcisk treści. O kolizję pyta baza: UPDATE IGNORE na kolumnie z kluczem UNIQUE przy konflikcie nie zmienia wiersza, więc duplikat kończy jako skipped z powodem, nigdy jako błąd MySQL.
Panel — przycisk „Przygotuj treści" (partia 10 pozycji). W Kroku 5 tę samą pracę przejmie cron.
Zmierzone, nie zadeklarowane
- runner: 11 zestawów, wszystkie zielone; 249 asercji w samym Kroku 3
- test mutacyjny: 71 z 72 mutacji zabitych, jedna odrzucona jako równoważna z dowodem
- odbiór na żywej instalacji: 49/49, powtarzalny; na 70 prawdziwych pozycjach 34 z gotową treścią, 36 odsianych, zero błędów MySQL, zero żądań sieciowych tam, gdzie kanał podał pełną treść
Naprawione po teście mutacyjnym
Kanał z popsutym kodowaniem przechodził filtr w całości: wyrażenie z modyfikatorem u na tekście z błędnym bajtem nie zgłasza błędu, tylko zwraca „nie ma". Dedup::valid_utf8() naprawia tekst na początku normalizacji.
AI News Portal 0.2.0 — Krok 2: Pobieranie
Drugi z siedmiu Krokow wtyczki AI News Portal. Wtyczka pobiera kanaly RSS, rozklada je na pozycje, odsiewa duplikaty i zapisuje material do wlasnej tabeli. Panel pokazuje zebrane pozycje i pozwala pobrac je recznie.
Co doszlo
- Http — timeouty 10/15/5 s, sufity 4 MB / 1 MB / 64 KB, 3 przekierowania, wykrywanie uciecia, blankietowy
robots.txtw transiencie per host (12 h). Zadania prosza oAccept-Encoding: identity, wiec sufit pamieci znaczy to samo na kazdym hostingu. - Feed — RSS 2.0, Atom i
content:encoded;DOCTYPEodrzucany w prologu przed parsowaniem (ochrona przed bomba encyjna), bezLIBXML_NOENT. - Dedup — normalizacja adresu i odcisk
sha256; 13 parametrow sledzacych i prefiksutm_zdejmowane przed porownaniem. - Runner — polowa zbierajaca:
INSERT IGNORE,wp_encode_emoji(), sufit 100 pozycji na kanal,try/catchper zrodlo i per pozycja. - Panel — Materialy i Ustawienia, przycisk "Pobierz teraz", cztery domyslne kanaly.
Poprawki po audycie odbiorczym
- P1 (powazne) — limit odpowiedzi liczyl bajty przed dekompresja, a kod porownywal dlugosc po. Kompletny kanal dostawal falszywy blad, a uciety gzip wracal jako binarna siczka.
- D1 — bramka
DOCTYPEskanowala caly dokument; artykul cytujacy HTML w CDATA kasowal caly kanal. - D2 —
guid isPermaLink="false"bral udzial jako adres artykulu, co grozilo druga kopia tego samego materialu.
Zmierzone, nie zadeklarowane
Runner 7/7 zestawow, 426 asercji w Kroku 2, 179/179 zabitych mutacji w siedmiu zestawach. Odbior na zywej witrynie: 40/40 — cztery prawdziwe kanaly, 70 pozycji, drugie pobranie zero nowych i 70 duplikatow, padniete zrodlo nie zatrzymalo reszty.
Wymaga: WordPress 6.5+, PHP 8.1+.
AI News Portal v0.1.0 - Krok 1: Fundament
AI News Portal v0.1.0 — Krok 1 „Fundament"
Pierwsze wydanie drugiej wtyczki z paczki dla klienta. Wtyczka instaluje się, zakłada swoją
tabelę, rejestruje typ treści i kategorie, wstawia jeden gotowy artykuł demo i dokłada dwie pozycje
w kokpicie. Nie pobiera jeszcze niczego z sieci — to zakres Kroku 2.
Co wchodzi
- Plik główny — nagłówek GPLv2 (bez „or later"),
Requires at least: 6.5,Requires PHP: 8.1,
Tested up to: 7.0.2, stałeAINP_*, jawna listarequire_once(bez autoloadera). - Tabela
wp_ainp_items—dbDelta,get_charset_collate(),LONGTEXTna treść i dwa
nazwane kluczeUNIQUE:url_hash(dedup po adresie) icontent_hash(dedup po treści,
z dozwolonym NULL). - Typ treści
ainp_article+ taksonomiaainp_topic—show_ui => true,
show_in_menu => false, archiwum/centrum-wiedzy/, artykuły/centrum-wiedzy/tytul/,
kategorie/centrum-wiedzy/kategoria/zywienie/. - Cykl życia — aktywacja rejestruje typy PRZED przeładowaniem reguł (adresy działają bez
ręcznego zapisu Bezpośrednich odnośników), wykrywa kolizję ze Stroną o slugucentrum-wiedzy
i ostrzega o niej w kokpicie; dezaktywacja wyrejestrowuje typy przed flushem i nie rusza danych. - Artykuł demo — tekst wpisany w kod, zero wywołań API, wstawiany dokładnie raz, z kategorią
i autorem. Dzięki niemu portal nie jest pusty przez pierwsze trzy Kroki. uninstall.phpbez śladu — wpisy, meta, powiązania, terminy, tabela, opcje, transienty
i crony. Dane wtyczki 1 pozostają nietknięte.LICENSE— pełny tekst GPL v2 skopiowany plikowo z rdzenia WordPressa.- Testy — 2 zestawy, 99 asercji, własny runner
run-tests-ai-news-portal.sh.
Poprawka planu, którą wymusiła żywa witryna
Plan zakładał kategorie pod /centrum-wiedzy/zywienie/, obok artykułów /centrum-wiedzy/tytul/.
W WordPressie to niewykonalne — obie struktury generują wzorzec centrum-wiedzy/([^/]+)/?$,
reguła artykułu stoi wyżej i archiwum kategorii zwracało 404. Kategorie dostały człon
kategoria, a taksonomia jest rejestrowana przed typem treści (reguła załączników CPT
przesłaniała adres kategorii). Adresy artykułów pozostały krótkie i niezależne od kategorii.
Audyt odbiorczy
0 blokerów. 6 znalezisk wagi DROBNE — wszystkie naprawione w tym wydaniu: trzy fałszywe zielenie
w testach (wykryte testem mutacyjnym na kopii kodu), artykuł demo bez autora przy aktywacji spoza
kokpitu, zadeklarowany text domain bez funkcji tłumaczeń, martwy menu_icon. Cztery długi
z przypisanymi Krokami spisane w STAN-WTYCZKA2.md.
Po naprawach: runner 2/2 zestawy, odbiór na dworek.local 39/39, 20/20 mutacji łapanych.
Następny Krok
Krok 2 „Pobieranie": Http, Feed, Dedup, zapis przez INSERT IGNORE, ekran Materiałów
z przyciskiem „Pobierz teraz".