· Case study · Szkoła kursów (CKKS) · PHP custom · 🌐 External client

ckks.pl — homepage 93 → 93 (score plateau + technical wins)

Drugi external case po kursybp.pl — ta sama firma macierzysta (Centrum Kształcenia Kadr Sportowych), ten sam legacy PHP stack (custom framework "ckks-admin2", edycja przez SSH). Re-crawl po fixes: homepage 93/100 baseline → 93/100 after fix (5 z 7 dimensions już max przed naszym iter 1). Score plateau jak kursybp.pl iter 3 — ale realna wartość w technical wins niewidocznych dla score: cache headers (CACHE_DISABLED + ETag + Last-Modified) dla LLM crawler efficiency, schema consolidation, oraz unique discovery: PHP syntax error w SSH append zablokował rendering homepage do 10 KB body. Restore przez backup pattern uratował dzień.

Homepage baseline 93 / 100 stan przed iter 1 — 5 z 7 dimensions już na max (structure 25, schema 25, llm_access 25, seo 19, authority 24)
Homepage after fix 93 / 100 score plateau — ceiling efekt; wartość iter 1 w technical wins zamiast punktach (cache, schema, anti-regression)
Avg 100-page crawl 78 → 79 baseline 78, after fix 79 (+1); sub-pages template kursy z NO_HEADINGS ciągną avg w dół (independent issue)
Technical wins 3 cache fixes session_cache_limiter, ETag, Last-Modified — niewidoczne w score, widoczne dla LLM crawlerów + Google PageSpeed
Unique discovery PHP syntax error append po otwartym `<?php` block → rendering broken → `php -l` + backup restore

Dlaczego ten case po kursybp.pl?

kursybp.pl pokazał że workflow Selpio + Claude Code działa dla legacy PHP. ckks.pl odpowiada na drugie pytanie: czy działa powtarzalnie?

Setup jest niemal identyczny:

Wynik repeatability check: tak — ten sam workflow per-issue prompts Selpio + Claude Code działa identycznie. Jedyna różnica: homepage baseline 93 (już 5 z 7 dimensions max) dał inne priorytety (cache headers, FAQ consolidation, copy refinement) niż kursybp.pl baseline 67 (gdzie dominowały: brak JSON-LD, brak robots.txt, brak sitemap.xml). Score plateau lekcja ta sama jak kursybp.pl iter 3: gdy score zbliża się do ceiling, fixes dają technical wins zamiast punktów.

Stan początkowy: homepage 93/100, avg 78 (100 pages)

Raport Selpio fresh crawl 2026-05-24 17:12 UTC. Homepage już 93/100, 5 z 7 dimensions już na max:

Avg crawl 78/100 dla 98 stron (2 failed 404). Sub-pages template kursy mają NO_HEADINGS / MISSING_H1 (35 stron) — to niezależny issue na poziomie template kurs.php, nie homepage.

Co naprawiliśmy w iter 1 (wszystkie na homepage):

Iter 1: 6 promptów z Selpio + SSH direct edit

Workflow per fix: backup pliku → edycja (heredoc / awk / Edit) → php -l syntax check → curl verify body length + grep schema markers → next.

Re-crawl 2026-05-24 18:59 UTC: homepage 93→93, avg 78→79. Score plateau — homepage już była max w 5 z 7 dimensions przed naszym iter 1. Verify produkcji: `Cache-Control: public, max-age=3600` ✓, ETag + Last-Modified headers ✓, 6 markerów Schema.org (FAQPage + EducationalOrganization) ✓, HTML body 46 KB, 8× h2 ✓. Realna wartość fixów w sekcji "Co dał ten case skoro score nie wzrósł" niżej.

Score plateau 93→93 — co dał ten case skoro score nie wzrósł?

Re-crawl po iter 1 dał identyczny score homepage (93/100) — wszystkie 5 z 7 dimensions już były na max przed naszym iter 1 (structure 25, schema 25, llm_access 25, seo 19, authority 24). Selpio analyzer nie może dać więcej punktów za fixes wzmacniające coś co już ma maksimum.

Ale to NIE znaczy że iter 1 był stratą czasu. Wartość fixów — identycznie jak w kursybp.pl iter 3 plateau:

Lekcja dla agency: komunikuj klientowi oczekiwaną zmianę score PRZED iteracją. Strona z baseline 93 (5/7 max) nie zyska 5 pkt — zyska technical infrastructure (cache, schema, anti-regression). Strona z baseline 67 zyska 25+ pkt szybko. To nie jest defect — to oczekiwana matematyka ceiling efektu.

Sub-pages problem: NO_HEADINGS na 35 stronach (independent issue)

Avg crawl 100 stron daje 79/100 — nisko mimo że homepage 93. Powód: sub-pages template kurs.php renderuje nazwę kursu w <div> zamiast <h1>, co Selpio analyzer flaguje jako critical NO_HEADINGS (35 stron) + MISSING_H1 (5 stron).

To jest znaleziony bug template, nie regresja z naszego iter 1: baseline crawl 17:12 UTC (przed naszymi fixami) dał identyczny avg 78 z tym samym pattern. Sub-pages mają inny rendering path niż homepage.

Naprawa wymaga osobnego iter 2 dotykającego src/strony/kurs.php template — wstrzymanego (klient decyduje czy poprawa avg score ma priorytet nad pozostałymi feature requests).

Lekcja: avg-page-score nie zawsze reflektuje "jakość strony" dla konkretnego query. Klient szukający w ChatGPT "kursy fitness Wrocław" trafi na homepage (93/100) lub sub-page kategorii (75/100), nie na specific kurs sub-page (60/100). Priorytetyzuj fixes per template, nie per pojedyncza strona.

Unique discovery: PHP syntax error w SSH append

Najciekawszy moment iter 1 (i lekcja dla każdego konsultanta agency pracującego z legacy PHP).

Po dodaniu FAQ HTML section do src/strony/start.php przez heredoc append, curl produkcji zwracał 10 KB body z zerem h-tagów. Snapshot przez browser potwierdzał: tylko nawigacja menu, body innerText 177 znaków.

Diagnoza przez php -l:

Parse error: syntax error, unexpected token "<",
expecting end of file in start.php on line 37

Przyczyna: oryginalny start.php kończy się otwartym <?php blockiem (`include 'components/opinie_slider.php'; require_once('start_wskazniki.php');` bez closing `?>`). Mój heredoc append dodał HTML <section>...</section> bezpośrednio do otwartego PHP block → fatal error → PHP rendering halt.

Fix: backup restore (cp start.php.bak start.php) + ponowny append z proper PHP closing/opening:

?>

<section class="faq">
  <h2>Najczęściej zadawane pytania</h2>
  ...
</section>

<?php

Lesson learned dla SSH direct edit: legacy PHP files często kończą się open <?php block bez closing tag (PEAR/PSR convention dla unikania trailing whitespace). Przed appendowaniem HTML zawsze sprawdź: tail -3 file.php + php -l file.php po każdej edycji. Backup pattern (file.php.bak-YYYY-MM-DD) ratuje sytuację gdy fatal error wystąpi po deploy.

Cache headers — niedoceniony win dla LLM crawlerów

Trzy z sześciu iter 1 promptów dotyczyły HTTP headers. To wyjątkowo wartościowy playbook dla każdej strony PHP/Node renderowanej server-side:

Sample kod PHP (4 linie, pasuje do każdej legacy strony):
session_cache_limiter("public"); session_cache_expire(60);
$mt = filemtime(__FILE__); header("Last-Modified: " . gmdate("D, d M Y H:i:s", $mt) . " GMT");
header("ETag: \\"" . substr(md5_file(__FILE__), 0, 16) . "\\"");

Schema.org FAQPage — homepage-conditional rendering

ckks.pl ma 30+ sub-pages (każdy kurs to osobna strona przez query string ?strona=kurs-X). Sub-pages mają własne logiki (Service Schema dla kursy.php, BreadcrumbList dla sub-section). FAQPage powinien być renderowany tylko na homepage, inaczej duplicate FAQ contextu dla AI.

<?php if (@$_GET["strona"] == "" && @$_GET["s"] == ""): ?>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [...6 Q+A pairs...]
}
</script>
<?php endif; ?>

Source of truth dla FAQ Q+A pairs to HTML <section class="faq"> w start.php (renderowany na homepage przez include logic). JSON-LD odzwierciedla 1:1 te same pytania i odpowiedzi — analyzer Selpio detektuje konsystencję Schema vs HTML (Jaccard similarity 80%+) i nie flaguje SCHEMA_HTML_MISMATCH.

Copy refinement — AMBIGUOUS_CLAIMS + INCOMPLETE_SENTENCES

Dwa info-level issues z analyzera Selpio, oba wymagają edycji prozy ręcznie (LLM nie napisze zgody prawnej autonomicznie):

Oba fixe były generowane przez per-issue prompty Selpio (free tier), nie wymagały mega-plan. Claude Code IDE pomógł wymyślić alternatywy formułowania zgodne z gramatyką polską i registerem branżowym.

Repeatability check: lekcja dla agency

Główny powód zrobienia ckks.pl jako drugiego external case (po kursybp.pl) to repeatability check — czy konsultant agency dostający klienta z tym samym stackiem może powtórzyć workflow w 30 minut zamiast debugować od zera?

Lekcje dla agency (porównanie z kursybp.pl)

🚀 Wygeneruj raport dla swojej strony Zobacz pozostałe case studies