# Changelog of RAG Content Crawler — Any Site to Clean Markdown (`eszetael_lab/rag-content-crawler`) Actor

- **URL**: https://apify.com/eszetael_lab/rag-content-crawler/changelog.md
- **Full Actor documentation**: https://apify.com/eszetael_lab/rag-content-crawler.md

## Changelog

### 0.1 — 2026-08-11

Wersja pierwsza wydana. Poniżej to, co doszło między budową a wydaniem — wszystko z rund
adwersaryjnych na zamrożonym kodzie i z czytania kodu własnymi oczami po każdej z nich.

- **Rezerwacja praw do eksploracji tekstu i danych (TDM) respektowana.** `robots.txt` mówi, czy
  wolno POBRAĆ stronę; rezerwacja mówi, czy wolno UŻYĆ jej treści do eksploracji — a po to właśnie
  powstaje Markdown dla modeli. Czytane z odpowiedzi, którą i tak pobieramy: nagłówek
  `TDM-Reservation`, `X-Robots-Tag: noai` oraz odpowiadające im `<meta>` w GŁOWIE dokumentu.
  Strona zastrzeżona nie jest konwertowana, dostarczana ani naliczana, a status nazywa ją i podaje
  konkretny sygnał. Skanujemy tylko głowę, żeby artykuł O rezerwacji nie uchodził za rezerwację.
- **Sufit na ciało odpowiedzi (8 MB, `robots.txt` 512 KiB).** Serwer po drugiej stronie jest cudzy
  i wybrany przez klienta, a czytanie całej odpowiedzi znaczyło, że strona odpowiadająca strumieniem
  bez końca kładzie kontener — po tym, jak klient zapłacił już za wcześniejsze strony. Najgrubsza
  z badanych stu stron miała 2,1 MB, więc sufit nie odcina niczego prawdziwego. Ucięcie kończy
  wspinaczkę po stopniach, zamiast wysyłać to samo ciało jeszcze raz drożej. Limit dla `robots.txt`
  jest wprost z RFC 9309 §2.5.
- **Zapis stanu po KAŻDEJ dostarczonej stronie**, nie co 25. Migracja platformy między zapisami
  gubiła do 24 stron ze stanu i po wznowieniu naliczała je DRUGI RAZ — przy domyślnym limicie 50
  stron to połowa przebiegu. Liczba 25 była wygodnym przybliżeniem, a przybliżenie w miejscu, gdzie
  idzie rachunek klienta, jest błędem.
- **Brama dostaw zamykana jako PIERWSZA rzecz przy migracji**, przed zapisem i przed rebootem.
  Wcześniej stała po udanym zapisie, więc nieudany zapis wychodził z obsługi z bramą otwartą:
  pętla dostarczała i naliczała dalej strony, których nie ma w stanie. Kierunek ryzyka był odwrotny
  do naszej zasady — przy niejednoznaczności traci klient, nie my.
- **Kod odpowiedzi z odpowiedzi, nie z nadziei.** Stopień przeglądarkowy wpisywał `httpStatus: 200`
  na sztywno, więc strona zwracająca 403 albo 404 trafiała do zbioru z rekordem, który KŁAMAŁ
  o tym, co powiedział serwer.
- **`<base href>` honorowany przy rozwijaniu linków** — ale kandydat na bazę przechodzi najpierw
  przez bramę adresów, żeby strona nie przestawiła nam punktu odniesienia na sieć wewnętrzną.
- **Brama adresów sprawdza KAŻDY skok przekierowania**, nie tylko adres wyjściowy, i rozpoznaje
  adres dosłowny po odkodowaniu procentowym oraz zapis `::ffff:127.0.0.1`. Kontrola na wejściu
  do labiryntu nie jest kontrolą labiryntu.

### 0.1 — budowa, 2026-08-10

Pierwsza wersja. Powstała po pomiarze, nie przed nim.

- **Odkrywanie linków z surowego HTML**, nie z oczyszczonego Markdownu. Markdown to treść PO
  usunięciu nawigacji — czyli po usunięciu miejsca, w którym dokumentacja trzyma odnośniki do
  dokumentacji. Bez tego `crawlDepth` nie działał i klient dostawał jedną stronę bez wyjaśnienia.

- **Brama adresów**: prywatne, pętlowe i link-lokalne odrzucane PO ROZWIĄZANIU NAZWY, bo nazwa
  może wskazywać na 127.0.0.1. Bez niej aktor byłby narzędziem do czytania metadanych chmury.

- **`robots.txt` i `Crawl-delay`** respektowane, z sufitem 10 s, żeby witryna prosząca o pięć minut
  nie zawiesiła przebiegu.

- **Adres końcowy po przekierowaniach** w rekordzie (`finalUrl`) i jako baza dla linków; po nim też
  deduplikacja, żeby `http://` i `https://` tej samej strony nie były naliczone dwa razy.

- **Świeży kontekst przeglądarki na każdą stronę** — ciasteczka i zgody nie przechodzą między nimi.

- **Rozpoznawanie stron-komunikatów**: rozbudowane „Access denied" nie uchodzi za treść, podnosi
  stopień, a jeśli mimo to zostanie — rekord niesie `contentWarning`.

- **Walidacja wejścia**: `crawlDepth: "abc"` kończy się zdaniem, nie śladem stosu.

- **Dynamiczna pamięć**: 512 MB, gdy przeglądarka jest wyłączona, 2048 MB gdy dozwolona. Rezerwacja
  2 GB na każdy przebieg przeczyłaby powodowi, dla którego ten aktor istnieje.

- **Wznawianie po migracji platformy.** Przeniesienie kontenera w trakcie przebiegu oznaczało
  start od zera i PONOWNE naliczenie stron, za które klient już zapłacił — ta sama wada, którą
  naprawialiśmy w bluesky i trends. Stan `delivered` w domyślnym magazynie, zapis co 25 stron
  i na wyjściu (zdarzenie platformy przychodzi co minutę, a krótki przebieg kończy się wcześniej),
  reboot **tylko po udanym zapisie**, brama dostaw zamykana przed rebootem. Przebieg, w którym
  wszystko było już dostarczone, kończy się **sukcesem** z wyjaśnieniem, nie porażką.

- **Schemat wyjścia** — dwa widoki, w tym „tylko Markdown" dla potoków osadzania.

- Trzy stopnie pobierania: zwykłe HTTP → odcisk TLS przeglądarki → Chromium. Przeglądarka startuje
  leniwie, przy pierwszej stronie, która jej naprawdę wymaga; przebieg po samej dokumentacji nie
  uruchamia jej ani razu.

- Eskalacja decydowana po TREŚCI, nie po kodzie odpowiedzi HTTP. Strona potrafi zwrócić `403`
  razem z pełną treścią i `200` z pustą skorupą.

- Zwracany jest NAJLEPSZY wynik ze wszystkich prób, nie ostatni — zmierzone przypadki, w których
  przeglądarka oddaje mniej niż zwykłe pobranie.

- Każdy rekord niesie `fetchedWith` i `tiersTried`, więc klient widzi, co go kosztowała ta strona.

- Rozliczenie za dostarczoną stronę; pusty przebieg kończy się porażką z powodem, nie zielonym zerem.
