7 czerwca 2026
Self-review loop — jak hooki Claude Code egzekwują SEO, zanim agent powie „gotowe"
- Claude Code
- AI
- SEO
Każdy publikowany artykuł albo buduje Twój autorytet tematyczny, albo po cichu go nadgryza. Strona bez linku wewnętrznego zawisa jako orphan page. Brakujące meta description to utracony fragment kontroli nad tym, co Google pokazuje w wynikach. Martwy link psuje crawl budget. Problemem nie jest znajomość tych reguł — to wie każdy, kto robi SEO. Problemem jest stosowanie ich za każdym razem, zwłaszcza gdy treść powstaje szybko i z pomocą agenta AI.
Ten artykuł opisuje mechanizm, który ten problem rozwiązuje u źródła — i robi to na własnym przykładzie. Nie zostałby zapisany jako gotowy, gdyby nie linkował do innego wpisu i nie miał kompletu pól SEO, bo przeszedł przez dokładnie ten sam self-review loop, który opisuje.
Co to jest self-review loop
To wzorzec, w którym agent AI sprawdza własną pracę względem twardych kryteriów, zanim wolno mu powiedzieć „gotowe”. Nie chodzi o to, żeby model „pamiętał” o regułach — modele bywają niekonsekwentne przy pracy na skalę. Chodzi o to, by reguły egzekwował deterministyczny strażnik, który nie ma gorszego dnia i nie zapomina.
W naszym przypadku tym strażnikiem jest bramka SEO dla artykułów bloga. Agent może napisać świetny tekst, ale jeśli zapomni linku wewnętrznego albo zostawi puste keywords — pętla odeśle go do poprawki, zanim cokolwiek trafi do publikacji.
Mechanizm: hooki i kod wyjścia 2
Claude Code ma system hooków — zewnętrznych skryptów, które Claude Code (Orchestrator) odpala w określonych momentach cyklu agenta. To nie kod wykonywany przez model, lecz deterministyczna komenda obok niego. Dwa zdarzenia są tu kluczowe:
| Hook | Kiedy się odpala | Do czego |
|---|---|---|
PostToolUse | po każdej edycji pliku | tanie, szybkie checki: lint, formatowanie, składnia |
Stop | gdy agent próbuje zakończyć odpowiedź | bramka jakości na koniec pracy — serce pętli |
Sercem self-review loop jest Stop. Działa przez kod wyjścia:
exit 0→ przepuść, agent normalnie kończy turę.exit 2→ agent nie może skończyć. Treść zstderr(kanał błędów procesu — wyjaśniony w legendzie pod tabelą) wraca do niego jako feedback i kontynuuje pracę.
Czyli: agent nie powie „gotowe”, dopóki kryteria nie są spełnione. To ta sama filozofia, co deterministyczna reguła kontra probabilistyczny agent, którą rozłożyłem na czynniki w tekście o Claude Code Skills vs Rules — hook to deterministyczna warstwa egzekucji nałożona na probabilistycznego wykonawcę.
Nasza bramka: co dokładnie sprawdza
Hook odpala się na końcu każdej tury — ale bramkę SEO aktywuje tylko gdy spełnione są łącznie trzy warunki:
- W turze użyto narzędzia
Edit,WritelubMultiEdit(cokolwiek było edytowane) - Ostatnia z tych operacji edycji dotyczyła pliku w
articles/*.md - Ten plik ma
draft: false
Jeśli którykolwiek nie jest spełniony — hook milczy i przepuszcza. Gdy wszystkie trzy zachodzą, sprawdza cztery binarne warunki:
| # | Warunek | Po co |
|---|---|---|
| 1 | ≥1 link wewnętrzny do innego artykułu | zero orphan pages, przepływ link equity |
| 2 | każdy link wewnętrzny prowadzi do realnego wpisu | brak martwych linków, ochrona crawl budgetu |
| 3 | description niepuste | kontrola nad snippetem w wynikach |
| 4 | keywords ma ≥1 frazę | wymuszony krok doboru fraz przed pisaniem |
Jeśli któregoś brakuje — exit 2 i konkretny komunikat: które pole jest puste, ilu linków brakuje, który link jest martwy. Agent dostaje listę i poprawia.
Twarde vs miękkie — i lekcja z kalibracji
Najważniejsza decyzja projektowa: co wolno egzekwować twardo, a co nie. Bramka deterministyczna może zawierać wyłącznie warunki w 100% binarne. Wszystko, co wymaga oceny redakcyjnej, musi zostać miękkim przypomnieniem — inaczej blokuje pracę bez sensu.
Przykład z naszego wdrożenia. Pierwotnie chciałem twardo wymóc, by fraza główna dosłownie występowała w tytule. Test na realnym korpusie ośmiu artykułów: cztery by nie przeszły, bo dobry tytuł bywa chwytliwym hookiem, a nie dopasowaniem frazy słowo w słowo. Twarda bramka na to blokowałaby połowę bloga przy każdym zapisie. Wniosek: ten warunek zszedł do warstwy miękkiej (nieblokującej — tylko przypomnienie, bez exit 2) — hook przypomina, żeby sprawdzić frazę w tytule/opisie, jakość anchor textów i kontekstowość linków, ale tego nie blokuje.
To granica, którą warto przemyśleć przy każdej bramce jakości: deterministycznie egzekwuj tylko to, co naprawdę jest zero-jedynkowe. Resztę zostaw człowiekowi jako sygnał, nie jako blokadę.
Zabezpieczenie przed pętlą
Skoro exit 2 zawraca agenta do pracy, grozi nieskończone „popraw → sprawdź → popraw”. Claude Code rozwiązuje to flagą stop_hook_active: przy drugim i kolejnym odpaleniu hook wie, że to już retry, i wtedy świadomie odpuszcza zamiast blokować w kółko. Jedna wymuszona iteracja naprawy, zero ryzyka zapętlenia. Dodatkowo skrypt działa fail-open — każdy jego własny błąd kończy się przepuszczeniem (exit 0), nigdy blokowaniem (exit 2), które zmuszałoby agenta do kolejnej próby naprawy. Strażnik, który się zepsuje, ma otwierać drzwi, a nie je zatrzaskiwać.
Wszystkie trzy ścieżki w jednym widoku — pogrubienie wskazuje, kto realizuje dany krok:
| Krok | Exit 0 — artykuł OK | Exit 2 — brak warunku (pętla) | Inny kod — błąd skryptu |
|---|---|---|---|
| Agent uznaje pracę za skończoną | Model (Claude) | Model (Claude) | Model (Claude) |
Claude Code uruchamia skrypt verify-article-seo.mjs | Claude Code | Claude Code | Claude Code |
| Hook parsuje transkrypt (zapis historii tury), sprawdza bramkę | Kod Node.js (deterministyczny) | Kod Node.js (deterministyczny) | Kod Node.js — crash/wyjątek |
| Wynik hooka | exit 0 + systemMessage na stdout* | exit 2 + lista braków na stderr** | exit 1 lub inny |
| Claude Code przetwarza wynik | Pokazuje systemMessage użytkownikowi, przepuszcza | Wstrzykuje stderr do agenta, blokuje zakończenie | Pokazuje błąd użytkownikowi, nie blokuje |
| Dalszy ciąg | Agent kończy turę — Claude Code | Agent poprawia plik — Model → hook odpala się ponownie | Agent kończy turę — Claude Code |
* stdout — standardowy kanał wyjścia procesu; Claude Code czyta go i przekazuje systemMessage użytkownikowi w interfejsie czatu.
** stderr — oddzielny kanał błędów; przy exit 2 Claude Code przechwytuje go i wstrzykuje do kontekstu agenta jako feedback do poprawki.
Co z tego masz w codziennej pracy
Dla osoby zarządzającej treścią: spójność SEO przestaje zależeć od tego, czy ktoś pamiętał o checkliście. Reguły — linkowanie wewnętrzne, komplet metadanych, brak martwych linków — są egzekwowane automatycznie, za każdym razem, niezależnie od tempa produkcji. To szczególnie wartościowe przy treści generowanej z pomocą AI, gdzie ilość rośnie szybciej niż uwaga recenzenta. (Tak samo zadbałem o spójność przy migracji domeny bez utraty SEO — różne mechanizmy, ten sam cel: nie tracić tego, co już zbudowane.)
Dla osoby technicznej: self-review loop to wzorzec przenośny daleko poza SEO. Wszędzie, gdzie masz obiektywne kryterium akceptacji — testy przechodzą, kontrakt API się zgadza, schema jest zgodna, brak sekretów w diffie — Stop hook z exit 2 zamienia „agent twierdzi, że skończył” w „agent skończył, bo kryteria są spełnione”. To różnica między ufaniem modelowi a weryfikowaniem go.
Mechanizm, nie gotowiec. Bramka jest tak dobra, jak kryteria, które w niej zakodujesz — i tak użyteczna, jak trafnie rozdzielisz to, co binarne, od tego, co wymaga ludzkiego oka.