01 SEO — desktop jasny · owner (klient) · 1440×1280 · zgięcie 810

Gate

runSeoGate · 8 rekordów · dump z Directus
3 z blockerami filtry
Wszystkie 8 Z blockerami 3 Artykuły 4 Strony 2 Autorzy 2
Rekord · slug · autorTypBlockeryRun
Lejek dwustopniowy: jak sprzedawać usługi bez presjilejek-dwustopniowy · andrzej-niemiec
article bez blockerów03:07
Intencja, nie keyword — jak czytać zapytania klientówintencja-nie-keyword · andrzej-niemiec
article
missing description missing sources
03:07
Nie sprzedajemy pakietów. Sprzedajemy wyniknie-sprzedajemy-pakietow · aion-niemiec
article bez blockerów03:07
Koszt pozyskania klienta (CAC) w usługach B2Bkoszt-pozyskania-klienta-cac · aion-niemiec
article
missing canonical missing Article JSON-LD
03:07
Strona główna/
page bez blockerów03:07
O nas/o-nas/
page
missing description
03:07
Andrzej Niemiecandrzej-niemiec
author bez blockerów03:07
Aion Niemiecaion-niemiec
author bez blockerów03:07
Gate blokuje npm run build (krok seo-gate → exit 1) dopóki lista blockerów nie jest pusta. Edycja rekordu → Directus. seo-gate · 1,8 s

Sitemap

sitemap-urls
11 URL
  • /2026-09-03
  • /o-nas/2026-09-01
  • /kontakt/2026-08-28
  • /pisze/2026-09-03
  • /pisze/lejek-dwustopniowy/2026-09-02
  • /pisze/intencja-nie-keyword/2026-08-30
  • /pisze/nie-sprzedajemy-pakietow/2026-08-27

llms.txt

/llms.txt
7 wpisów
Nie jest ranking gate. Plik informacyjny dla crawlerów AI — nie wpływa na wynik gate ani na build. Regenerowany przy każdym data.
# Aion Marketing

> Marketing usług B2B bez presji: lejek dwustopniowy, intencja zamiast keywordów.

Site: https://aion-marketing.pl/

## Articles

- [Lejek dwustopniowy](https://aion-marketing.pl/pisze/lejek-dwustopniowy/): jak sprzedawać usługi bez presji
- [Intencja, nie keyword](https://aion-marketing.pl/pisze/intencja-nie-keyword/)
- [Nie sprzedajemy pakietów](https://aion-marketing.pl/pisze/nie-sprzedajemy-pakietow/): sprzedajemy wynik
- [Koszt pozyskania klienta (CAC)](https://aion-marketing.pl/pisze/koszt-pozyskania-klienta-cac/)

## Authors

- [Andrzej Niemiec](https://aion-marketing.pl/autor/andrzej-niemiec/)
- [Aion Niemiec](https://aion-marketing.pl/autor/aion-niemiec/)
Kopiuj

Hreflang — deferred (PL-only)

Tenant jednojęzyczny: globals.locales = ["pl"]. Sekcja uaktywni się po dodaniu drugiego locale i nie liczy się do gate. brak drugiego locale

02 SEO — desktop operator · operator · data-theme=operator · 1440×1280

Gate

runSeoGate · 8 rekordów · dump z Directus
3 z blockerami filtry
Wszystkie 8 Z blockerami 3 Artykuły 4 Strony 2 Autorzy 2
Rekord · slug · autorTypBlockeryRun
Lejek dwustopniowy: jak sprzedawać usługi bez presjilejek-dwustopniowy · andrzej-niemiec
article bez blockerów03:07
Intencja, nie keyword — jak czytać zapytania klientówintencja-nie-keyword · andrzej-niemiec
article
missing description missing sources
03:07
Nie sprzedajemy pakietów. Sprzedajemy wyniknie-sprzedajemy-pakietow · aion-niemiec
article bez blockerów03:07
Koszt pozyskania klienta (CAC) w usługach B2Bkoszt-pozyskania-klienta-cac · aion-niemiec
article
missing canonical missing Article JSON-LD
03:07
Strona główna/
page bez blockerów03:07
O nas/o-nas/
page
missing description
03:07
Andrzej Niemiecandrzej-niemiec
author bez blockerów03:07
Aion Niemiecaion-niemiec
author bez blockerów03:07
Gate blokuje npm run build (krok seo-gate → exit 1) dopóki lista blockerów nie jest pusta. Edycja rekordu → Directus. seo-gate · 1,8 s

Sitemap

sitemap-urls
11 URL
  • /2026-09-03
  • /o-nas/2026-09-01
  • /kontakt/2026-08-28
  • /pisze/2026-09-03
  • /pisze/lejek-dwustopniowy/2026-09-02
  • /pisze/intencja-nie-keyword/2026-08-30
  • /pisze/nie-sprzedajemy-pakietow/2026-08-27

llms.txt

/llms.txt
7 wpisów
Nie jest ranking gate. Plik informacyjny dla crawlerów AI — nie wpływa na wynik gate ani na build. Regenerowany przy każdym data.
# Aion Marketing

> Marketing usług B2B bez presji: lejek dwustopniowy, intencja zamiast keywordów.

Site: https://aion-marketing.pl/

## Articles

- [Lejek dwustopniowy](https://aion-marketing.pl/pisze/lejek-dwustopniowy/): jak sprzedawać usługi bez presji
- [Intencja, nie keyword](https://aion-marketing.pl/pisze/intencja-nie-keyword/)
- [Nie sprzedajemy pakietów](https://aion-marketing.pl/pisze/nie-sprzedajemy-pakietow/): sprzedajemy wynik
- [Koszt pozyskania klienta (CAC)](https://aion-marketing.pl/pisze/koszt-pozyskania-klienta-cac/)

## Authors

- [Andrzej Niemiec](https://aion-marketing.pl/autor/andrzej-niemiec/)
- [Aion Niemiec](https://aion-marketing.pl/autor/aion-niemiec/)
Kopiuj

Hreflang — deferred (PL-only)

Tenant jednojęzyczny: globals.locales = ["pl"]. Sekcja uaktywni się po dodaniu drugiego locale i nie liczy się do gate. brak drugiego locale

03 SEO — stany · loading · empty · error · blocked · 1440×780
Loading skeleton · shimmer 1200 ms · bez opacity
Empty świeży tenant · checklist runbooka, nie ilustracja

Gate nie ma jeszcze czego sprawdzać

Dump z Directus jest pusty. Cztery kroki z runbooka montażu — po ostatnim „Uruchom gate” zapełni tabelę.

  1. Uzupełnij globals: siteName, description, organization runbook §3
  2. Dodaj autora z bio i sameAs (Person JSON-LD) runbook §4
  3. Opublikuj pierwszy artykuł z description i źródłami articles → wpisy
  4. Uruchom gate: npm run build lub przycisk u góry seo-gate
Sitemap i llms.txt wygenerują się z tym samym dumpem. Otwórz Directus
Error gate przerwany · retry + log, nie „coś poszło nie tak”
seo-gate zakończył się błędem (exit 1) ostatni run nieudany

Dump z Directus nie ma pola articles[].author w oczekiwanym kształcie — gate przerwał przed policzeniem blockerów. Poprzedni wynik (03:07) pozostaje widoczny jako STALE.

$ node packages/seo/dist/seo-gate.js --dump .aios/dump.json
[seo-gate] loaded 4 articles · 2 authors · 2 pages
TypeError: Cannot read properties of undefined (reading 'slug')
    at hasAuthor (seo-gate.js:31:22)
    at runSeoGate (seo-gate.js:33:18)
exit 1 · 0,4 s
Blocked LOCAL-GATED · brak klucza = BLOCKED, nie „partial done”

Gate zablokowany — brak DIRECTUS_TOKEN BLOCKEDUNVERIFIED

Bez tokenu serwisowego panel nie pobierze dumpu kolekcji articles · authors · pages · globals. Nie pokazujemy częściowego wyniku: tabela, sitemap i llms.txt pozostają puste do czasu odblokowania.

# .env tenanta (tylko operator)
DIRECTUS_URL=https://cms.aion-marketing.pl
DIRECTUS_TOKEN=<brak>
05 SEO — spec · decyzje, badge vs cisza, LIVE/UNVERIFIED, komponenty

Decyzje projektowe — SEO

  1. Gate jest pierwszy, bo blokuje build. Tabela GateTable zajmuje lewą kolumnę nad zgięciem; Surface preview po prawej jako kontekst „jak to wygląda w Google”. Sitemap i llms.txt pod zgięciem — to dowody, nie decyzje.
  2. Blocker = chip krytyczny z ikoną „×” i tekstem z kodu (missing description, dosłownie jak w seo-gate.ts). Nie tłumaczymy nazw blockerów — operator grepuje je w logu builda.
  3. Rekord bez blockerów nie ma badge'a. Cisza = muted check + „bez blockerów”. Zielony pojawia się tylko na StatusTile z dowodem (seo-gate, sitemap-urls, generateLlmsTxt) i znacznikiem LIVE · hh:mm.
  4. llms.txt niesie notkę „nie jest ranking gate” w treści karty, nie w tooltipie — żeby klient nie traktował go jako KPI. Przycisk „Kopiuj” zamiast edycji: plik jest generowany.
  5. Hreflang = karta z przerywanym obrysem i statusem „deferred (PL-only)” ze źródłem globals.locales. Nie znika z IA (ta sama IA w obu trybach), ale nie udaje aktywnej funkcji.
  6. Licznik znaków w SERP-card (title 61/60 → warn tylko na liczniku). Kolor statusu wyłącznie na liczniku, tytuł zostaje w --blue jak w wyniku wyszukiwania.

Co jest badge'em, co ciszą

SygnałFormaDowód
Rekord z blockeramichip .blk per blocker, --criticalrunSeoGate().blockers[]
Rekord OKcisza: muted check „bez blockerów”ok: true
Gate ogółemStatusTile critical + badge „3 z blockerami · LIVE”agregat gate
Sitemap / llms.txtStatusTile ok + LIVE · 03:07dist/sitemap.xml, dist/llms.txt
HreflangStatusTile unknown (przerywana krawędź) „deferred (PL-only)”globals.locales
Gate nieudanyerror-panel + log + retry; poprzedni wynik jako STALEexit code ≠ 0
Brak tokenublocked-banner „BLOCKED · UNVERIFIED”, kafle w trybie dimDIRECTUS_TOKEN unset

Dane LIVE / UNVERIFIED na planszy

ElementŹródłoEtykieta
tenant, sites, mode, pluginymodules.json, clients.json (BRIEF §6)LIVE
slugi artykułów i autorówrepo fixtures/ · BRIEF §6LIVE
nazwy blockerówpackages/seo/src/seo-gate.tsLIVE
tytuły, description, daty publikacjiprzykład redakcyjnyUNVERIFIED
liczba URL w sitemap (11), czasy 03:07snapshot planszyUNVERIFIED
format llms.txt (nagłówki, sekcje)packages/seo/src/llms-txt.tsLIVE

Tokeny — decyzja koloru akcentu

--accent-op = ciemny miód (jasny motyw) / jasne złoto (operator). Różni się od --warn (ochra) jasnością, nie tylko odcieniem — po desaturacji primary CTA i badge warn nie zlewają się. --accent-cl = głęboki granat (jasny) / jasny błękit (operator), różny od --teal. --purple zadeklarowany, nieużyty.

--accent-op --warn --accent-cl --teal --ok --critical --blue --muted

Użycie --accent na planszy: aktywna pozycja menu systemu, primary CTA, focus ring. Nic więcej. Aktywna zakładka i toggle „on” idą przez --text, żeby akcent nie konkurował ze statusem.

Shell — AIONLINE-CONTRACT

Menu systemu 244 px po lewej (brand „AIOS Admin”, tenant switcher tylko dla operatora, 12 pozycji IA §1, stopka: ModeBadge + build). AionLine 56 px po prawej: systemy Hub · Discovery · Panel · Create, inbox ekosystemu, obecność, profil, wyloguj, rozsuń. Topbar produktu bez dzwonka i profilu. Commerce w menu z kłódką i tooltipem „kill-gate Discovery”.

Board 01 = rola client/owner (jasny, tenant statyczny, brak akcji operatorskich). Board 02 = operator (data-theme="operator", switcher tenantów, pełne akcje). Ta sama IA w obu — różnią się tylko uprawnieniami.

Komponenty użyte (BRIEF §5) i hierarchia

.status-tile ×4 (pasek) · .gate-table (Gate) · .tabs (filtry gate, SERP/JSON-LD/OG) · .skeleton (loading) · .empty-checklist (empty) · .blocked-banner (blocked) · .mode-badge (stopka menu) · .stat-card = alias .status-tile. Nieużyte celowo: .conceal (brak sekretów w SEO), .kill-lock (tylko menu Commerce), .chart-bars, .event-stream, .diff-panel, .drawer.

Hierarchia: h1 26 px bold → StatusTile (mono 28 px) → tytuły sekcji 15 px/650 → treść 13 px → meta 11 px. Dwa poziomy zagnieżdżenia: karta na tle, tabela/lista w karcie. Obrys 1 px tylko na wierszach, przyciskach, polach, pickerze. Odstęp między sekcjami 20 px w kolumnie, 36 px między blokami logicznymi (pasek → grid). Operator (02) ma identyczny układ — różnica to switcher tenantów i akcja „Otwórz Directus”.