Kompleksowy Audyt Infrastruktury p24-infra — 2026-07-08

Status dokumentu: ✅ UKOŃCZONY (wszystkie 15 sekcji, 2026-07-08) — scalony do main. 🔴 Wymaga przeczytania i eskalacji przez człowieka do 2026-07-09 wieczorem (patrz docs/priorities.md). Nic z rekomendacji §12 nie zostało jeszcze wdrożone — to audyt + plan, nie gotowy kod. Jeśli wracasz do tego dokumentu po kompresji kontekstu — zacznij od tabeli statusu (§0.3) i streszczenia zarządczego (§1).


§0. Metodologia i zakres

0.1 Cel

Pełny, wielowymiarowy audyt p24-infra zlecony przez użytkownika 2026-07-08, obejmujący: organizację, procesy, narzędzia, luki proceduralne, dispatcher/kolejkę workerów, planowanie pojemności (zakup nowego serwera), gotowość produkcyjną 3. licencji Claude dla EcoTrans, specyfikację dashboardu klienckiego, przegląd systemu ról/CLAUDE.md, oraz redesign systemu zgłaszania incydentów (w tym alerting SLA — dopisane w toku sesji).

Nie jest to audyt od zera. p24-infra ma już bogatą bazę: docs/analiza-infrastruktury.md (2026-06-27), docs/strategia-p24-infra-2026.md (2026-06-27), docs/priorities.md (żywy, 71 sesji historii), 242 playbooki w docs/playbooks/, oraz 18-perspektywową taksonomię w docs/01-*docs/18-*. Ten dokument buduje na tym, odświeża co się zdezaktualizowało (11+ dni i ~15 sesji od ostatniej strategii), i pogłębia tematy, których tamte dokumenty nie pokrywają (capacity planning nowego HW, 3. licencja Claude w produkcji, dashboard klienta, redesign incydentów, SLA alertów).

0.2 Metoda

  • Sekcje badawcze (dispatcher, role/CLAUDE.md, alerting/SLA, incydenty, capacity, monitoring maili/WhatsApp) — zlecone równolegle jako subagenty badawcze (Explore/general-purpose), wyniki syntetyzowane tutaj.
  • Sekcje projektowe (redesign incydentów, dashboard klienta, gotowość 3. licencji, roadmapa) — projektowane bezpośrednio na podstawie wymagań użytkownika + danych z sekcji badawczych.
  • Każda sekcja kończy się Kluczowe ustalenia + Rekomendacje (P0/P1/P2).
  • Dokument zapisywany na bieżąco (commit po każdej ukończonej sekcji) — odporność na kompresję kontekstu.

0.3 Tabela statusu sekcji — SPRAWDŹ TO NAJPIERW

#SekcjaStatusNotatka
0Metodologia i zakresten plik
1Streszczenie zarządcze✅ donepatrz §1
2Inwentaryzacja architektury✅ donepatrz §2 — odsyła do istniejącej taksonomii 18-perspektyw
3Audyt i unifikacja dokumentacji✅ donepatrz §3 — P0 już wykonane: linki dodane do docs/index.md
4System ról Claude (CLAUDE.md, prompty, local/remote)✅ donepatrz §4 — znaleziono 4 niezależne systemy ról
5Głęboka analiza dispatchera/kolejki✅ donepatrz §5 — w tym §5.6 auto-close issues
6Alerting SLA — czemu 20 zgłoszeń/dobę wisi✅ donepatrz §6 — znaleziono definitywną przyczynę
7Redesign zgłaszania incydentów (infra + security)✅ donepatrz §7
8Capacity planning — nowy serwer✅ donepatrz §8 — strategia już mówi: bms-3 najpierw, HW dopiero gdy się wysyci
9Gotowość produkcyjna — 3. licencja Claude / MCP w produkcji EcoTrans✅ donepatrz §9 — w tym architektura lekkich workerów jednorazowych
10Dashboard kliencki (Portal) — monitoring maili/WhatsApp✅ donepatrz §10 — portal dziś to jeden płaski panel, zero multi-tenant
11Synteza słabości organizacyjnych/procesowych✅ donepatrz §11 — 4 wzorce przekrojowe
12Rekomendacje i roadmapa priorytetowa✅ donepatrz §12 — 32 pozycje P0/P1/P2
13Załączniki / surowe dane z agentów✅ donepatrz §13
14Higiena równoległości sesji + stacja dewelopera jako węzeł mocy✅ donepatrz §14 (dopisane w toku sesji)
15Pinbox24 — stabilność redeploymentu, Angular vs Next.js, środowisko staging✅ donepatrz §15 — staging już istnieje (bms-3→bms-4), decyzja Angular/Next.js nadal otwarta

Legenda: ✅ done · 🔵 in progress · ⏳ pending · 🔴 blocked (patrz notatka)


(Sekcje poniżej wypełniane w miarę postępu — każda aktualizacja tego pliku zmienia też tabelę statusu powyżej.)

§1. Streszczenie zarządcze

Co zlecono

Kompleksowy audyt p24-infra: słabości organizacyjne/procesowe/narzędziowe, luki proceduralne, głęboka analiza kolejki workerów, planowanie zakupu nowego serwera, gotowość 3. licencji Claude dla produkcji EcoTrans, dashboard monitoringu dla klienta, przegląd systemu ról i CLAUDE.md, redesign zgłaszania incydentów — plus dwa pytania dopisane w toku sesji: analiza SLA alertowania (dlaczego ~20 zgłoszeń/dobę wisi) i czy PR-y automatycznie zamykają issues.

Top 10 ustaleń

  1. SLA 2-3 min dla alertów nigdy nie istniało dla naprawy — tylko dla wstępnej inwestygacji AI. Realna naprawa to raz dziennie (nightly-devops-triage, 20:00 UTC). To jednoznacznie odpowiada na główne pytanie użytkownika z tej sesji. (§6)
  2. Redundancja dispatchera jest w większości iluzoryczna — bms-3 nieudany, dev-laptop niepewny, vps-i1 ma 1 slot zarezerwowany za monitoringiem. bms-4 to de facto jedyny realny punkt przepustowości. (§5.3)
  3. System alertów potrafi cicho anulować zadanie bez ŻADNEGO powiadomieniacancel_unroutable_nc_alert_batch() po 15 min, zero śladu. (§6.3)
  4. Auto-close PR→issue DZIAŁA poprawnie dla standardowego pipeline’u (10/10 w próbce, 1 sekunda po merge) — to NIE jest przyczyna zaległości, wbrew początkowemu podejrzeniu. (§5.6)
  5. 4 niezależne, niesynchronizowane systemy definicji ról Claude — w tym 3 różne pliki “secret-manager” bez wspólnego źródła prawdy. Tylko 4/10 ról mają dowiedzioną ścieżkę zdalnego wykonania. (§4)
  6. Zgłaszanie incydentów rozproszone po 5+ mechanizmach bez wspólnego schematu kto/maszyna/sesja/dlaczego — ale jeden z nich (hook + agent_errors) już robi niemal dokładnie to, czego chce użytkownik, tylko dla jednego wzorca. (§7)
  7. Strategia z 2026-06-27 już odradza zakup nowego serwera teraz — rekomenduje dokończenie bms-3 (koszt: 0 dodatkowy) i dopiero gdy się nasyci, OVH Rise-3 (16 vCPU/64GB, ~€150/mies. — nie 24-core, dla którego brak uzasadnienia). (§8)
  8. Zapytania jednorazowe (brand-strategy, 150k in/20k out) nie powinny dzielić floty OAuth-seat workerów kodujących — inny profil obciążenia, inny model rozliczeń (API-key vs subskrypcja). Właściwy szkielet już zaprojektowany, ale niezbudowany (migracja 048). (§9)
  9. Dashboard klienta wymaga budowy od zera — dane źródłowe istnieją (MongoDB dla e-mail, Supabase dla WhatsApp), ale bez agregacji, bez modelu multi-tenant, bez precedensu RLS per-klient w całym repo. (§10)
  10. Wzorzec przekrojowy: nowa praca dopisywana obok, nie do, istniejącej struktury nawigacyjnej — widoczny w rolach, dokumentacji dispatchera, incydentach i samym docs/index.md (nie linkował do najważniejszych dokumentów strategicznych — już naprawione w tej sesji). To najważniejsza słabość procesowa całego audytu. (§11.1)

Top rekomendacje natychmiastowe (pełna lista: §12)

  • Skorygować mentalny model SLA alertów (dokumentacja, nie kod) — dziś, zero kosztu.
  • Naprawić ciche anulowanie nc-alert-batch (Discord + komentarz GH).
  • Scalić 4 systemy ról do jednego źródła prawdy w repo.
  • Dokończyć bms-3 PRZED jakąkolwiek decyzją o zakupie nowego serwera.
  • Rozszerzyć schemat incydentów o reporter/machine/session — bez nowego endpointu, rozszerzając już działający hook.

Dopisane w toku sesji (§14-15)

  • §14 — Równoległość sesji i stacja dewelopera jako węzeł mocy: dwa z trzech pytań użytkownika mają już działające rozwiązania w repo (hook blokujący mieszanie branchy od 2026-07-08; dev_r_agent_sessions + hooki SessionStart/Stop już śledzą sesje lokalne). Trzecie (dopisanie komputera dewelopera do floty workerów) zaprojektowane na sprawdzonym wzorcu dev-laptop (WSL2 + autossh), z priorytetem nocnym już wspieranym przez istniejący schemat (max_weight_night).
  • §15 — Pinbox24: redeploy dziś kruchy (dopiero od 2-3 dni w drodze do powtarzalności); EOL Ubuntu 20.04 na bms-1 to blokada bezpieczeństwa niezależna od decyzji Angular/Next.js; decyzja “przepisać czy zostać” wciąż otwarta mimo pozornie rozstrzygającej nazwy dokumentu; środowisko staging już istnieje na bms-3 (zdegradowane) — rekomendacja to przenieść na bms-4, nie kupować nowego serwera.

Status dokumentu

Wszystkie 15 sekcji ukończone (2026-07-08, ta sesja — w tym 2 dopisane w toku: §14, §15). Branch docs/comprehensive-infra-audit-2026-07 gotowy do przeglądu przed PR do main. Zobacz §13 dla surowych danych źródłowych z agentów badawczych.

§2. Inwentaryzacja i mapa architektury

Pełna mapa architektury już istnieje i jest solidna — docs/index.md definiuje 18-perspektywową taksonomię (01-architecture18-workbooks), każda z własnym README.md. Nie duplikuję tego tutaj; ten audyt rozszerza ją o tematy, których 18 perspektyw nie pokrywa wprost (capacity planning nowego HW, 3. licencja Claude w produkcji, dashboard klienta, redesign incydentów — patrz §5-10) i o deltę od ostatniego przeglądu strategicznego (analiza-infrastruktury.md / strategia-p24-infra-2026.md, oba 2026-06-27, czyli ~11 dni / ~15 sesji temu — patrz docs/priorities.md dla pełnej historii sesji 57-71 w tym okresie).

Najważniejsza obserwacja inwentaryzacyjna — opisana szczegółowo w §3 — to że sam docs/index.md (nawigacyjny hub) nie linkuje do najważniejszych dokumentów strategicznych repo (analiza-infrastruktury.md, strategia-p24-infra-2026.md, docs/audits/INDEX.md). To jest jednocześnie inwentaryzacyjna luka i temat do unifikacji.

§3. Audyt i unifikacja dokumentacji

3.1 Struktura dziś — trzy równoległe systemy organizacji

  1. 18-perspektywowa taksonomia (docs/01-*docs/18-*) — czysta, każdy folder ma README.md, linkowana z docs/index.md. To jest właściwy “hub nawigacyjny”.
  2. 241 playbooków w docs/playbooks/ — płaska lista, bez podfolderów, bez własnego indeksu tematycznego (jest tylko przez odnośniki z ról w docs/roles/).
  3. 112 luźnych plików .md bezpośrednio w docs/ — poza taksonomią 18-perspektyw, w tym najważniejsze dokumenty strategiczne tego repo: analiza-infrastruktury.md, strategia-p24-infra-2026.md, priorities.md, portal.md, infrastructure-overview.md, dziesiątki dokumentów *-operations.md.

Zweryfikowane bezpośrednio: docs/index.md nie zawiera ani jednego linku do analiza-infrastruktury.md, strategia-p24-infra-2026.md, ani docs/audits/INDEX.md. Ktoś nawigujący repo przez oficjalny “hub” (docs/index.md → “Quick Navigation”) nigdy nie trafi na te trzy dokumenty, mimo że są to jedne z najważniejszych źródeł strategicznych w całym repo. To jest dokładnie ten rodzaj rozjazdu, o którym mówi §5.3 (sprzeczna dokumentacja dispatchera) i §4.1 (4 równoległe systemy ról) — wzorzec przekrojowy: nowe dokumenty/systemy powstają obok istniejącej struktury nawigacyjnej, zamiast być do niej dopisywane.

3.2 Konkretne działania unifikacyjne (wykonane w ramach tego audytu)

  • ✅ Ten dokument dodany do docs/audits/INDEX.md (zrobione przy tworzeniu szkieletu).
  • 🔲 Do zrobienia (rekomendacja P0 poniżej): dopisać sekcję do docs/index.md z linkami do analiza-infrastruktury.md, strategia-p24-infra-2026.md, docs/audits/INDEX.md — np. nowy wiersz w tabeli “Quick Navigation”: “Strategia i audyty repo → INDEX.

3.3 Inne ustalenia unifikacyjne (z sekcji 4-10 tego audytu, zebrane tu przekrojowo)

  • System ról (§4): 4 niezależne definicje ról bez wspólnego źródła prawdy — rekomendacja scalenia agent-prompts/roles/*.md do docs/roles/<rola>/. Rekomendacje P0/P1
  • Dispatcher (§5.3): docs/worker-queue-operations.md sprzeczny z docs/playbooks/queue-dispatcher-priority.md co do tego, które serwery są aktywne. Rekomendacja P0
  • Incydenty (§7.1): 5+ równoległych mechanizmów zapisu bez wspólnego schematu — rekomendacja ujednolicenia pod jedną tabelą Supabase z widokami generowanymi.

Kluczowe ustalenia (§3)

  1. Sama struktura dokumentacji (18-perspektyw + playbooki + luźne pliki) jest generalnie dobra, ale nawigacja do niej jest niekompletna — najważniejsze dokumenty są “niewidoczne” z głównego huba.
  2. Wzorzec powtarzający się w całym repo (role, dispatcher-docs, incydenty, teraz też ten sam audyt): nowa praca dopisywana obok, nie do, istniejącej struktury nawigacyjnej. To jest właściwie jedno z głównych ustaleń organizacyjnych całego audytu — patrz §11.

Rekomendacje (§3)

  • P0 — Dopisać do docs/index.md sekcję linkującą analiza-infrastruktury.md, strategia-p24-infra-2026.md, docs/audits/INDEX.md (i ten dokument po scaleniu).
  • P1 — Rozważyć krótki “changelog dokumentacji” w docs/index.md (data ostatniej dużej aktualizacji strategii) — dziś nie widać z huba, że strategia ma 11+ dni i wymaga odświeżenia względem tego audytu.
  • P2 — Playbooki (241 plików płasko w docs/playbooks/) mogłyby zyskać własny tematyczny indeks (analogiczny do docs/audits/INDEX.md) — dziś jedyny sposób ich odkrycia to przeszukiwanie przez role (docs/roles/<rola>/index.md) albo pełny listing katalogu.

§4. System ról Claude — CLAUDE.md, prompty inicjujące, playbooki, local vs remote

4.1 Najważniejsze ustalenie: nie 2, a 4 niezależne, niesynchronizowane systemy ról

#SystemLokalizacjaKto to ładujeRole
AGłówna persona interaktywnaC:\Users\konar\.claude\agent-prompts\roles\*.md (11 plików, tylko Windows, nie w git)Skille ~/.claude/commands/role-<name>.mdsys-admin, sys-security, secret-manager, dev-coder, dev-reviewer, dev-tester, marketing-director, content-creator, video-producer, designer, + p24-network-admin (sierota, brak skilla)
BRepo-lokalny indeks playbookówdocs/roles/<rola>/index.md (10 katalogów, w git)Nic tego programowo nie ładuje — to dokument dla ludzi, wskazujący z powrotem na ścieżkę systemu ATe same 10 co A, minus p24-network-admin
CCross-repo loader (/open-role).claude/roles/*.md (4 pliki) + .claude/role-contexts/<repo>/<rola>.md (11 plików, w git)Skill ~/.claude/commands/open-role.mdsysadmin, debugger, security-auditor, secret-manager — inne nazwy, debugger nie istnieje nigdzie indziej
DZdalny headless workerinfra/agent-prompts/*.md (7 plików, w git, realnie wdrożone na /opt/p24-infra/infra/agent-prompts/)scripts/spawn-worker.sh przez ROLE=infra|dev|orchestrator + job_type + JOB_PROFILEZgrubny podział 3-drożny, NIE ten sam 10-rolowy podział

To jest realny problem, nie tylko kosmetyczny. Trzy odrębne pliki “secret-manager” istnieją w tym repo (A, C, D) bez wspólnego źródła — każdy inny w treści.

4.2 Tabela pokrycia per rola (systemy A/B/D — 10 kanonicznych ról)

Rola(a) init/skill (A)(b) charter (A)(c) playbook index (B)(d) udowodniona ścieżka zdalna (D)
sys-admin✅ szczegółowy✅ obszernyinfra-task-request-worker.md
sys-security❌ brak — żaden job_type/ROLE nie mapuje się na promptu security-workera
secret-manager✅ obszernyworker-secret-manager.md v2.0.0 — ale to inna treść niż wersja A
dev-coderworker-issue.md, worker-nextjs-dev.md, worker-python-dev.md
dev-reviewerreview-pr-worker.md
dev-tester🟡 cienki (charter urwany po scope)❌ brak — żaden dedykowany prompt workera
marketing-director❌ brak
content-creator🟡 worker-content.md istnieje, ale bez mapowania credentiali
video-producer❌ brak
designer❌ brak
p24-network-admin (poza “10”)❌ brak skilla✅ (odsyła do docs/playbooks/p24-network-admin.md)❌ brak katalogu docs/roles/❌ brak

4.3 Konkretne niespójności

  1. p24-network-admin jest realny, ale wpół-podłączony. Wymieniony w macierzy ról p24-infra/CLAUDE.md §Role Enforcement (linia 252), ma pełny plik persony i playbook operacyjny — ale brak skilla /role-p24-network-admin, brak indeksu w docs/roles/, nieobecny w obu tabelach “10 ról” (docs/roles/README.md i docs/playbooks/roles-and-skills.md). Można go dziś aktywować tylko ręcznie wklejając ścieżkę pliku w prompt Agent(). Klasyczny dryf: dodany do jednego rejestru, nigdy nie rozpropagowany do pozostałych trzech.
  2. Trzeci, niezależnie wymyślony system ról istnieje w .claude/roles/ (sysadmin, debugger, security-auditor, secret-manager), z osobnymi kontekstami per-repo dla pinbox24/brandpilot/art-agency/et-operational-platform/p24-infra. Jego secret-manager.md to inny plik niż wersje z systemów A i D — trzy odrębne definicje “secret managera” bez wspólnego źródła. debugger i security-auditor (z formalnym protokołem izolacji sesji/adwersarialnego audytu) nie mają odpowiednika nigdzie w A/B.
  3. Zdalne workery NIE MOGĄ dziś załadować systemu A w ogóle. Sam roles-and-skills.md (linia 98) to przyznaje: ścieżka promptu to zawsze C:\Users\konar\.claude\agent-prompts\roles\[rola].md … “lub na VPS-ach jeśli zsynchronizowane”. Żaden mechanizm synchronizacji nie istnieje — ścieżka jest w katalogu domowym użytkownika Windows, nigdy niecommitowana do git, nigdy niewdrażana przez secrets-sync.yml ani żadną rolę Ansible. W praktyce zdalne workery claude -p ładują wyłącznie system D — strukturalnie inny prompt (lazy-load przez GH API), który dzieli intencję, ale nie treść z systemem A. Tylko 4 z 10 ról (sys-admin, secret-manager, dev-reviewer, dev-coder) mają jakikolwiek zdalny odpowiednik — dev-tester i wszystkie 4 role kreatywne mają zero ścieżki headless-worker.
  4. docs/roles/<rola>/index.md to czysta dokumentacja — nieużywana przez żaden skill ani worker programowo.

Kluczowe ustalenia (§4)

  1. Odpowiedź na pytanie użytkownika “czy role mają zdefiniowane prompty inicjujące, manuale, playbooki” — tak, ale w 2-4 niespójnych miejscach jednocześnie, bez jednego źródła prawdy.
  2. Odpowiedź na pytanie “czy role odpalą się lokalnie i zdalnie” — nie w równym stopniu: tylko 4 z 10 ról (sys-admin, secret-manager, dev-coder, dev-reviewer) mają dowiedzioną ścieżkę zdalną; sys-security, dev-tester i wszystkie 4 role kreatywne działają tylko lokalnie na Windows, mimo posiadania pełnej persony/playbooka.
  3. p24-network-admin to rola “widmo” — zdefiniowana, ale niedostępna przez normalny interfejs /role-*.
  4. Trzecia, w pełni zbudowana ale kompletnie odłączona taksonomia ról (/open-role) tworzy ryzyko: ktoś może odkryć i użyć niewłaściwego “secret-managera”.

Rekomendacje (§4)

  • P0 — Scalić systemy A+B: przenieść treść agent-prompts/roles/*.md do repo (docs/roles/<rola>/persona.md), zastępując dzisiejszy nagłówkowy stub w index.md. To jedyny sposób, żeby zdalne workery kiedykolwiek mogły załadować tę samą personę co sesja lokalna — dziś jest to fizycznie niemożliwe (ścieżka Windows-only).
  • P0 — Nadać p24-network-admin brakujący skill /role-p24-network-admin.md i dopisać go do obu tabel “10 ról” (robiąc z nich “11 ról”), albo jawnie udokumentować że jest wyzwalany wyłącznie zdalnie przez network-ops task-playbook.
  • P1 — Rozstrzygnąć los systemu C (.claude/roles/ + /open-role): albo wchłonąć koncepcje debugger/security-auditor (realnie przydatne — izolacja sesji nie istnieje w A/B) do kanonicznych 10 ról, albo jawnie oznaczyć system C jako zastąpiony/deprecated, żeby nikt przypadkiem nie użył jego wersji secret-managera.
  • P1 — Rozszerzyć pliki credentiali workera (role-<nazwa>.env) o sys-security i dev-tester — dziś te dwie role nie mają żadnej ścieżki zdalnego wykonania mimo bycia częścią macierzy ról bezpieczeństwa/jakości.
  • P2 — Po scaleniu A+B, zaktualizować roles-and-skills.md żeby usunąć mylące sformułowanie “jeśli zsynchronizowane” i zastąpić je faktycznym stanem.

§5. Głęboka analiza kolejki workerów / dispatchera

5.1 Architektura przepływu

Issue trafia do kolejki dwiema drogami: (a) cykliczny wave-builder w meta-dispatcher (co ~4h, batch, z topologicznym sortowaniem zależności/nakładania plików, Kahn’s algorithm, max 6 issues/wave), (b) ad-hoc POST /queue-issue. CF Worker klasyfikuje rolę/repo/ preferowany serwer, liczy wagę i priorytet z labeli, odrzuca duplikaty (dedup.ts — już-zmergowane lub już-w-locie issues są pomijane), i wstawia wiersz do dev_r_worker_queue (Supabase).

Konsumpcja to model push, nie pull, sterowany centralnie: queue-dispatcher.sh (co 2 min, systemd timer) bierze HA lease (migracja 035, TTL 150s), odzyskuje zawieszone zadania (>2h) przez reset_stale_workers(), ładuje dev_r_server_capacity, i przekazuje do queue-dispatcher-loop.py (1939 linii) — właściwej logiki: health-gating serwerów po heartbeat (healthy/degraded/down), ceiling wagi floty (odcina zadania playwright gdy zdrowa pojemność <75%/<25%), preferencja małych hostów (vps-i1/vps-h1/dev-laptop) przed bms-4 dla lekkich zadań, i budżet RAM per-serwer: available_ram_gb = (os_ram_gb - reserved_ram_gb) * 0.80 minus już zarezerwowane przez claimed/running. Eskalacja błędów: light → heavy → playwright → orchestrator → failed, max 5 prób spawn przed trwałym failem i eskalacją komentarzem na GH. Tryb nocny (20:00–02:59 UTC) podmienia limity na max_workers_night/max_weight_night.

5.2 Katalog historycznych awarii (z 6 przeanalizowanych playbooków)

AwariaPrzyczyna źródłowaPowtórzyła się?Trwałość naprawy
Dispatcher stall (dispatcher-keyerror-stall.md)Migracja dodaje kolumnę do dev_r_server_capacity, którą Python czyta, ale bash SELECT w queue-dispatcher.sh jej nie pobiera → KeyError przed pierwszym logiem, cicho połkniętyWzorzec (brakująca kolumna w SELECT) powtórzył się w innej postaci (patrz dead-column poniżej)Częściowa — zapobieganie to przypis w dokumentacji, nie automatyczny check
Metadata JSON crashSupabase REST zwraca metadata jako string JSON, dispatcher wołał .get() bez json.loads()Naprawione raz (PR #1657), ale bez reguły lint wymuszającej bezpieczny idiom na przyszłośćŁatka-na-wzorzec, nie systemowa
OOM loopreset_stale_workers() resetował zawieszone zadania do queued BEZ inkrementacji retry_count → nieskończona ~2h pętlaNie powtórzyła się od migracji fixTrwała — naprawiona na poziomie funkcji SQL (jedno źródło prawdy)
Wyłączenie dispatchowania nc-alert (2026-07-01/02)Wielo-przyczynowe: CF Worker 500, stale heartbeat vps-i1 → auto-cancel zadań hard-afinowanych do vps-i1, GH Actions maskował HTTP 5xx jako ::warning:: (zielony run mimo failed dispatch)Pierwsze wystąpienie udokumentowane; maskowanie było “PR pending” w momencie pisania playbooka — wymaga weryfikacji czy już scaloneNie w pełni trwała — maskowanie i hard-affinity do jednego hosta to nadal otwarte punkty w samym playbooku
Dead-column referencesMigracja 045 usunęła stare kolumny RAM, ale fallback .get(<usunięta>, default) cicho maskował żywą wartość stałą — błąd cichy, nie crashujący (over/under-subscription niezauważalny)Drugie wystąpienie wzorca “migracja usuwa kolumnę, kod nadal się do niej odwołuje”Naprawiona w bieżącym kodzie, ale wciąż brak automatycznej bramki CI wykrywającej martwe kolumny
RAM-gate exhaustionreset_stale_workers() odwoływał się do nieistniejącej kolumny server_label (realna: server_node) → RPC cicho failował co cykl → zawieszone zadania nigdy nie zwalniane → budżet RAM permanentnie ujemnyNaprawione migracją 037Trwała — ale to trzeci wariant tego samego wzorca

Wzorzec przekrojowy (3 z 6 playbooków): zmiana schematu (dodanie/usunięcie/zmiana nazwy kolumny) cicho psuje dispatch, bo referencja do kolumny jest zduplikowana w trzech miejscach (bash SELECT, Python .get(), funkcja SQL) bez jednego źródła prawdy i bez CI, które sprawdza spójność wszystkich trzech. To nie pojedynczy incydent — to powtarzający się kształt przyczyny źródłowej.

5.3 Wąskie gardła i pojedyncze punkty awarii

  • bms-4 to jedyny host z pojemnością heavy/orchestrator. Awaria bms-4 = zero przepustowości realnego dev/fix (lekkie zadania porządkowe nadal chodzą gdzie indziej). Fix kolejności dispatch na bms-4 (PR #2450, issue #2422) nadal niescalony.
  • nc-alert-batch jest hard-afinowany do vps-i1 (czyta lokalny Prometheus). Awaria vps-i1 cicho zawiesza całą triage alertów infra, bez automatycznego przekierowania — playbook sam to flaguje jako nierozwiązane.
  • Rozjazd dokumentacji odzwierciedla realną kruchość, nie tylko nieaktualność: docs/worker-queue-operations.md twierdzi że vps-i1 jest “disabled” a bms-4 to “jedyny aktywny host workerów”, podczas gdy queue-dispatcher-priority.md (aktualizowany 2026-07-01) i docs/priorities.md opisują flotę 3-węzłową (vps-i1/vps-h1/dev-laptop light + bms-4 heavy). Ktoś czytający tylko jeden z tych dokumentów w trakcie incydentu źle oceni dostępną redundancję.
  • bms-3 jako 3. węzeł dispatch nie powiódł się operacyjnie (spawn failed exit=255 dla wszystkich zadań, #2147) — wyłączony 2026-06-30. Playbook prowizji jest solidny, ale węzeł nigdy nie wrócił online — planowana redundancja nie istnieje dziś realnie.
  • dev-laptop jako 4. węzeł jest osiągalny tylko przez niezweryfikowany od 2026-07-08 tunel autossh — pojemność istnieje na papierze w dev_r_server_capacity, nie w praktyce.
  • Wniosek: mechanizm HA lease (migracja 035) jest poprawny na poziomie kodu, ale praktyczny failover (vps-i1 “disabled” wg jednego dokumentu, bms-3 nieudany, dev-laptop niepewny) sprawia, że bms-4 jest de facto pojedynczym punktem awarii dla realnej przepustowości dev/orchestrator.

5.4 Pojemność — ile mamy zapasu

  • bms-4: 32 GB RAM, 4 GB rezerwy → 22,4 GB do zabookowania (cap 80%). Koszt wagi: light 3 GB, heavy 6-8 GB (dokumentacja niespójna — 8 vs 6 GB), playwright 6 GB, orchestrator 12 GB.
  • vps-i1: tylko ~7,4 GB total, ~3,2 GB zajęte przez stack monitoringu → efektywnie zapas na 1 lekkiego workera (emergency_max_workers=1).
  • Przykładowy scenariusz z dokumentacji: 1 orchestrator (12 GB) + 1 heavy GHA job (8 GB) zostawia 2,4 GB; dodanie kolejnego heavy job już wchodzi na minus (RAM-gated). System pracuje blisko nasycenia przy zaledwie jednym nakładającym się orchestrator/heavy fan-out — to nie jest margines na skoki obciążenia, to jest normalny stan.
  • Logika odcinania (playwright blokowany <75% zdrowej pojemności) przy flocie 2-3-węzłowej uruchamia się już po utracie jednego hosta — bardzo mało luzu przed automatyczną degradacją.

5.5 Luki nieopisane w żadnym playbooku

  • Brak bramki CI sprawdzającej, że migracja zmieniająca kolumnę w dev_r_server_capacity/ dev_r_worker_queue jest odzwierciedlona wszędzie, gdzie kolumna jest czytana — to potwierdzony powtarzający się root cause (3 z 6 playbooków). Realny fix: krok CI, który porównuje referencje kolumn w kodzie z information_schema.columns.
  • Maskowanie HTTP 5xx jako ::warning:: w nc-alert-instant-dispatch.yml — status napraw nieznany, wymaga potwierdzenia (żywa dziura: CF Worker 500 = zielony run GH Actions).
  • Możliwy race condition między czyszczeniem stale-locka orchestratora (>10 min uznawane za martwe) a genuinie wolnym przebiegiem (np. GitHub API rate-limit) — mogłoby podwoić kolejkowanie tej samej fali (częściowo łagodzone przez dedup per-issue, ale nie z założenia).
  • Dokumentacja niespójna co do tego, które serwery są aktywne — samo w sobie ryzyko operacyjne podczas incydentu.
  • bms-3 i dev-laptop to “planowana redundancja, która dziś nie działa” — żaden alert nie mówi wprost “oczekiwany 3./4. węzeł nieobecny”, tylko pośrednie sygnały enabled=false/stale heartbeat, które człowiek musi zauważyć.

Kluczowe ustalenia (§5)

  1. Redundancja dispatchera jest w większości udokumentowana, nie udowodniona w praktyce — realnie działa tylko bms-4.
  2. Trzy niezależne historyczne awarie mają ten sam kształt źródłowy (rozjazd schematu między bash/Python/SQL) — to systemowa luka procesowa, nie seria pechowych przypadków.
  3. Dokumentacja operacyjna dispatchera jest wewnętrznie sprzeczna (worker-queue-operations.md vs queue-dispatcher-priority.md vs priorities.md) co do tego, które serwery żyją.
  4. System pracuje strukturalnie blisko nasycenia RAM nawet bez awarii — jeden nakładający się orchestrator + heavy job już wyczerpuje budżet.

Rekomendacje (§5)

  • P0 — Zunifikować docs/worker-queue-operations.md z realnym stanem dev_r_server_capacity (który host jest faktycznie enabled=true) — jedno źródło prawdy, reszta linkuje.
  • P0 — Potwierdzić czy maskowanie HTTP 5xx w nc-alert-instant-dispatch.yml zostało naprawione; jeśli nie — to żywa P0 dziura w widoczności awarii.
  • P1 — Dodać krok CI/pre-merge, który przy migracji dotykającej dev_r_server_capacity lub dev_r_worker_queue wymusza grep referencji kolumn we wszystkich trzech miejscach (bash, Python, SQL) — zamyka powtarzający się root cause na stałe, nie punktowo. Powiązane: 7 redesign incydentów — ten sam mechanizm CI mógłby też auto-zgłaszać incydent gdy wykryje rozjazd. Powiązane z §5.5 (playbook provision-bms-3-dispatch-node.md).
  • P1 — Zdecydować los bms-3 (albo dokończyć re-provision wg playbooka z 2-tygodniowym soakiem, albo formalnie zdjąć z dev_r_server_capacity żeby nie sugerować pojemności, której nie ma) — patrz też §8 (capacity planning nowego serwera — może to być tańsza alternatywa niż zakup nowego HW).
  • P2 — Alert dedykowany: “oczekiwany węzeł dispatch nieobecny >X h” zamiast polegania na człowieku zauważającym enabled=false.

5.6 Czy merge PR automatycznie zamyka issue? (pytanie dopisane w toku sesji)

Dobra wiadomość — dla standardowego pipeline’u dev-issue to działa poprawnie. Zweryfikowano empirycznie: repo domyślny branch to main, worker-template (worker-issue.md) używa Closes #N w treści PR (nie w commicie — tam poprawnie Implements: #N do changelogu), plus dodatkowy jawny gh issue close gdy zadanie przyszło z kolejki (QUEUE_ROW_ID>0). Próbka 15 ostatnich merged PR: 10/15 zamknęło powiązany issue automatycznie, dokładnie 1 sekundę po merge — to podpis w pełni automatycznego zamknięcia, nie ręcznego. Pozostałe 5/15 to PR-y pisane ręcznie w trakcie sesji (rotation logi, podsumowania batch), które celowo nie zamykają (np. issue tylko częściowo naprawiony, albo pending human-action) — nie jest to awaria mechanizmu.

Wniosek: auto-close NIE jest przyczyną narastania zaległości dla standardowej ścieżki worker → PR → merge. Realną przyczyną jest to, co opisano w §6 poniżej — auto-close działa, ale dotyczy tylko klasycznych issues dev-issue, nie alertów p24-infra-nc-alert (te w ogóle nie przechodzą przez ten sam cykl życia — patrz §6).

/production i /staging to legalny fallback dla ścieżek, które nie przechodzą przez standardowy dev-issue worker (infra-task, ręcznie rozwiązane human-action) — nie trzeba ich zmieniać.

Rekomendacja (drobna, P2): ujednolicić PR-y pisane ręcznie w sesji, żeby używały Closes #N (lub jawnie Part of #N (tracking) gdy zamknięcie byłoby błędem) zamiast pozostawiać to przypadkowi.

§6. System alertowania — analiza SLA i przyczyn zaległości

Pytanie użytkownika: “dlaczego żaden automat u nas nie zdejmuje tych automatycznych zgłoszeń, tylko problem i dlaczego one wiszą długo nierozwiązane […] a mamy w infra automaty, mamy 20 zgłoszeń od doby”.

6.1 Rzeczywisty pipeline (z realnymi opóźnieniami, nie aspiracyjnymi)

1. Alert się odpala                                          czas do powstania issue
   - prometheus-alerts-ai-triage.yml (co 15 min)              ≤15 min
   - db-maintenance-check.yml (dziennie/tygodniowo)           ≤24h / ≤7 dni
   - health-check.yml (co 2h, osobny label "server-down")     ≤2h
       ↓ powstaje GH issue z labelem p24-infra-nc-alert

2. nc-alert-instant-dispatch.yml (na event issues.labeled)    sekundy–15 min
       ↓ POST do CF Worker → kolejka job_type=nc-alert-batch,
         weight=light, HARD afinowany do vps-i1

3. queue-dispatcher.sh (pętla 2 min)                          ≤2 min — JEŚLI
   ale TYLKO gdy vps-i1 ma wolny slot (emergency_max_workers=1)  jest wolny slot

4. nc-alert-orchestrator uruchamia się                        zmienne
   → TYLKO INWESTYGUJE. Jego własna specyfikacja mówi wprost:
     "NIGDY nie zamykasz issues, NIGDY nie SSH-ujesz żeby naprawić,
     NIGDY nie dispatchujesz fix-workerów [...] Naprawianie robi
     nightly-devops-triage." Zostawia komentarz + label ai-dev-queued.

5. FAKTYCZNA naprawa: nightly-devops-triage                   do 24h
   Uruchamia się RAZ DZIENNIE (cron bms-4, 20:00 UTC). To JEDYNY
   krok, który realnie naprawia/zamyka/eskaluje alert.

6. Auto-zamknięcie po ustąpieniu: prometheus-alerts-ai-triage  ≤15 min
   (ta sama pętla 15-min) — to jedyna naprawdę szybka i działająca pętla.

6.2 Definitywna odpowiedź: czy istnieje SLA 2-3 min?

Nie — to oczekiwanie nigdy nie zostało zaimplementowane dla naprawy. Realnie działający mechanizm 2-3 min (kroki 2-3) dowozi tylko komentarz inwestygacyjny, nigdy naprawę ani zamknięcie — to świadomy projekt (nc-alert-orchestrator.md explicite tego zabrania). Jedyny komponent uprawniony do faktycznej naprawy/eskalacji to nightly-devops-triageraz dziennie, nie raz na 2 minuty. SLA 2-3 min istnieje dla “AI zostawi komentarz inwestygacyjny”; nie istnieje dla “problem zostanie naprawiony albo eskalowany do człowieka”.

6.3 Druga, cichsza przyczyna: nc-alert-batch potrafi zniknąć bez śladu

nc-alert-batch jest hard-afinowany WYŁĄCZNIE do vps-i1 (potrzebuje lokalnego localhost:9090 Prometheusa — bms-4 nie ma tam dostępu). vps-i1 ma dokładnie 1 slot workera (emergency_max_workers=1, zarezerwowany za stackiem monitoringu). Gdy ten slot jest zajęty albo heartbeat vps-i1 jest stale, cancel_unroutable_nc_alert_batch() cicho anuluje zadanie po 15 minutach (NC_ALERT_STRAND_MINUTES=15) — bez komentarza GH, bez Discorda, bez eskalacji do człowieka. Issue zostaje z samym labelem, bez żadnej aktywności orchestratora. Dokładnie to wydarzyło się 2026-07-01/02 (7 issues, w tym 2 CRITICAL EndpointDown, bez przetworzenia przez 6-12h, zero powiadomienia). Poprzednia naprawa (zamiana ::warning:: na exit 1 + alert Discord przy CF Worker ≠ 201/202) jest już w kodzie — ta konkretna dziura zamknięta. Ale ciche 15-minutowe anulowanie bez fallbacku na bms-4 nadal istnieje.

6.4 Dlaczego akurat ~20/dobę się kumuluje

To nie jest głównie problem hałasu/duplikatów — prometheus-alerts-ai-triage.py ma stabilny dedup_key() i sprawdza istniejący otwarty issue przed utworzeniem nowego. To kombinacja trzech realnych przyczyn:

  1. Luka projektowa — nawet w pełni zdrowy pipeline dowozi tylko “zbadane, komentarz dodany” w ciągu minut; faktyczna naprawa czeka na jedyny dzienny batch. 20 nowych/dobę łatwo przewyższa przepustowość jednego batcha dziennego.
  2. Twardy sufit pojemności — 1 slot workera na jedynym hoście, który w ogóle może uruchomić krok inwestygacji.
  3. Luka routingu — ciche anulowanie po 15 min bez fallbacku (patrz §6.3) oznacza, że część alertów nie dostaje NAWET inwestygacji.

Kluczowe ustalenia (§6)

  1. To jest odpowiedź na pytanie użytkownika wprost: żaden administrator “nie powinien” przejmować zgłoszeń w 2-3 minuty, bo taki mechanizm nigdy nie istniał dla naprawy — istnieje tylko dla wstępnej inwestygacji przez AI. Oczekiwanie 2-3 min było nieporozumieniem co do zakresu istniejącego mechanizmu, nie jego awarią.
  2. Realny cykl naprawy to do 24h (raz dziennie), nie 2-3 minuty.
  3. Istnieje żywa, cicha dziura: alerty mogą zniknąć bez ŻADNEJ inwestygacji, jeśli jedyny slot na jedynym uprawnionym hoście jest zajęty.

Rekomendacje (§6)

  • P0 — Skorygować mentalny model, zanim cokolwiek zmienimy w kodzie. Zaktualizować docs/priorities.md / dokumentację operacyjną: instant-dispatch dowozi komentarz inwestygacyjny w 2-15 min; naprawa jest raz dziennie (20:00 UTC). To samo w sobie usuwa źródło frustracji “dlaczego to wisi” jeszcze przed zmianą kodu.
  • P0 — cancel_unroutable_nc_alert_batch() musi przestać cicho anulować. Przy anulowaniu: komentarz GH + standardowe powiadomienie Discord/bug (wg §Error Notification Standard w CLAUDE.md). To zamyka dokładnie tę samą dziurę co incydent 2026-07-02, wciąż otwartą.
  • P1 — Dodać bms-4 jako fallback dla nc-alert-batch, gdy vps-i1 niezdrowy/nasycony (wymaga dostępu do Prometheusa z bms-4 — np. przez publiczną trasę Caddy z poświadczeniem SOPS, bo port 9090 jest zafirewallowany). Powiązane z §5.3 (bms-4 jako pojedynczy punkt awarii — to dodatkowy argument za tym, żeby nie polegać na jednym hoście).
  • P2 — Dać nightly-devops-triage dodatkowy, lekki przebieg śróddzienny (co 4-6h, ograniczony wyłącznie do zamykania/eskalacji issues p24-infra-nc-alert już oznaczonych ai-dev-queued przez orchestrator) zamiast czekać pełne 24h. To pojedyncza zmiana najbardziej zbliżająca realne opóźnienie do oczekiwania użytkownika.
  • P2 — Poprawić nieaktualny tekst w nightly-devops-triage.md sugerujący “co godzinę” (relikt po zmianie harmonogramu) — kosmetyczne, ale utrwala błędne przekonanie o SLA.

§7. Redesign systemu zgłaszania incydentów

7.1 Stan obecny — dokładna mapa (ustalona badawczo)

ŹródłoKtoMaszynaKiedySesjaDlaczego (komentarz)Ustrukturyzowane?
gh issue create ręcznie (incident-management.md)pośrednio (timestamp issue)✅ (wolny tekst)❌ czysta proza
docs/incidents/*.md (1 plik istnieje)✅ (proza)✅ (proza)❌ brak frontmatter
docs/pinbox24/incident-register.md + template✅ (tabela nagłówkowa)✅ (sekcja root cause)🟡 częściowo
services/scripts/open-incident.pydev_r_incidents (Supabase)✅ (opened_at)🟡 (notes wolny tekst)✅ realny INSERT, ale brak kolumn reporter/machine/session
docs/secrets-rotation-log.md (git, aktywnie używany)🟡 (Rotator — wolny tekst)🟡 (czasem w tekście Rotator)✅ bogaty (Reason)🟡 pół-strukturalne (komórki tabeli, nie queryable)
agent_errors (Supabase) — auto-filowane przez hook post-bash-error-capture.sh🟡 (worker label)❌ brak kolumny hostname✅ (ts)session_id, realna kolumna🟡 (fix_hint to szablon per wzorzec)✅ najbardziej strukturalne ze wszystkich — i jedyne, które odpala się samo

Najważniejsze ustalenie: żadne z tych źródeł nie ma wspólnego schematu kto/maszyna/sesja/dlaczego, i nie ma klucza łączącego je ze sobą (agent_errors.session_id nie pojawia się nigdzie indziej). “Maszyna” jako pole strukturalne nie istnieje nigdzie — tylko domyślana z etykiety worker albo z prozy. To dokładnie ta sama skarga użytkownika: “zapis do pliku to za mało” — nawet ścieżki oparte na Supabase kodują tożsamość jako wolny tekst, nie jako krotkę actor+machine+session.

7.2 Ważna korekta założenia: nie ma “secret-manager MCP”

Operacje na sekretach dziś działają jako wzorzec roli/persony (spawnowany Agent() przyjmujący roles/secret-manager.md), nie jako serwer MCP z własnym cyklem życia tool-call. Hook nie może więc podłączyć się “pod secret-manager MCP” wprost — hooki podłączają się pod generyczne PreToolUse/PostToolUse na Read/Bash/PowerShell (.claude/settings.json).

Dobra wiadomość: mechanizm bardzo zbliżony do tego, czego chce użytkownik, już istnieje i działa.claude/hooks/post-bash-error-capture.sh (+ odpowiednik PowerShell) na każdym PostToolUse Bash/PowerShell, dla wzorca “credential w URI” (scheme://user:pass@host): redaguje wartość, zapisuje strukturalny wiersz do agent_errors (session_id, worker, environment, tool_name, pattern_id, severity), automatycznie tworzy GH issue z labelem security+bug, i dispatchuje do kolejki secret-manager. To realna, już działająca ścieżka auto-zgłoszenia — ale tylko dla jednego wektora ekspozycji.

Dwie luki, które to ogranicza:

  1. Bramkowane exit-code’em — hook uruchamia dopasowanie wzorca tylko gdy komenda zakończyła się błędem. Udana komenda, która przypadkiem wypisze sekret (np. Read na pliku poświadczeń który nie jest zablokowany, albo udane sops -d bez flag wypisujące na stdout), nigdy nie dotrze do tego hooka.
  2. Brak PostToolUse na Read w ogóle — istnieje tylko PreToolUse (blokada-przed- -wczytaniem). Sekret, który wycieknie przez dozwolony Read, nie ma żadnej warstwy wykrycia po fakcie.

7.3 Propozycja redesignu — dwa tory

Tor A — Incydenty infrastrukturalne (awarie, dispatcher, alerty, downtime):

  • Zachować GH Issues jako warstwę human-readable/workflow-trigger (już działa dobrze — patrz §5.6, auto-close działa poprawnie).
  • Ujednolicić zapis pod spód: jeden nowy widok/tabela Supabase dev_r_incident_log (rozszerzenie istniejącego dev_r_incidents) z obowiązkowymi kolumnami: reporter (kto/co — human email albo claude-session:<id>), machine (hostname/IP — dziś nieobecne nigdzie), session_id (klucz łączący z agent_errors — dziś nieobecne poza agent_errors), context (jaka rola/zadanie), comment (dlaczego), reported_at. Skrypt open-incident.py już robi realny INSERT — trzeba tylko rozszerzyć schemat i wymusić wypełnienie tych pól (nie opcjonalne).
  • Wszystkie istniejące log-i tekstowe (docs/incidents/, docs/pinbox24/incident-register.md, docs/secrets-rotation-log.md) stają się widokami wygenerowanymi z tabeli, nie osobnymi źródłami prawdy — koniec z ręcznym dopisywaniem wierszy Markdown.

Tor B — Incydenty bezpieczeństwa / credential exposure:

  • Rozszerzyć już-działający hook post-bash-error-capture.sh (i jego odpowiednik PowerShell) o dwie zmiany: (a) skanowanie treści tool_response niezależnie od exit code (nie tylko przy błędzie), (b) nowy PostToolUse hook na Read skanujący zawartość odczytanych plików pod kątem wzorców sekretów (te same regexy co dziś, rozszerzone).
  • Każde trafienie → ten sam potok co dziś (redakcja + agent_errors + auto-GH-issue + dispatch do kolejki secret-manager), ale z dodanymi kolumnami reporter/machine/ comment (dziś brakującymi nawet w agent_errors).
  • To NIE wymaga nowego serwera MCP — rozszerza istniejący, już-sprawdzony mechanizm hooków
    • Supabase REST, który de facto już jest tym “endpointem”, o który prosi użytkownik (POST do Supabase REST, nie do nowego, dedykowanego HTTP API). Budowanie osobnego endpointu od zera dodałoby powierzchnię ataku i utrzymania bez korzyści — Supabase REST
    • RLS już to zapewnia.

Kluczowe ustalenia (§7)

  1. Dziś istnieje 5+ równoległych, niesynchronizowanych mechanizmów zapisu incydentów, każdy z innym podzbiorem pól — dokładnie potwierdza skargę użytkownika.
  2. Jeden z nich (agent_errors + hook) już robi niemal dokładnie to, o co prosi użytkownik (auto-zgłoszenie strukturalne z session_id) — ale tylko dla jednego wzorca i tylko przy błędzie polecenia.
  3. Nie trzeba budować nowego HTTP endpointu — Supabase REST już nim jest; brakuje jedynie wspólnego schematu i rozszerzenia zakresu hooków.

Rekomendacje (§7)

  • P0 — Rozszerzyć agent_errors (lub nową dev_r_incident_log) o kolumny reporter, machine, comment jako NOT NULL — dziś nawet najbardziej strukturalna tabela ich nie ma.
  • P0 — Rozszerzyć hook o skanowanie niezależne od exit code + nowy PostToolUse na Read (patrz §7.2) — to zamyka lukę “udana komenda wycieka sekret bez wykrycia”.
  • P1 — Zbudować widoki SQL/skrypt generujący docs/secrets-rotation-log.md i docs/pinbox24/incident-register.md z tabeli zamiast ręcznego dopisywania — koniec rozjazdu między “co się stało” a “co jest zapisane”.
  • P1 — Dodać session_id jako wspólny klucz obcy między agent_errors, dev_r_incidents i rotation logiem — dziś nie da się skorelować zdarzeń między tabelami.
  • P2 — Rozważyć krótki dashboard (Grafana albo portal) czytający z ujednoliconej tabeli, żeby “kto/kiedy/dlaczego” było widoczne bez grzebania w trzech miejscach.

§8. Capacity planning — zakup nowego serwera

8.1 Dane wyjściowe (zweryfikowane w kodzie/dokumentacji)

SerwerRdzenieRAMmax_workers dziśRola
bms-18 vCPU32 GB0 (brak agenta)Pinbox24 prod (Ubuntu 20.04 EOL)
bms-28 vCPU32 GB0MongoDB rs0 PRIMARY
bms-38 vCPU32 GB0 dziś; strategia proponuje 3MongoDB SECONDARY + Pinbox24 staging
bms-48 vCPU32 GBmax_workers=4, emergency_max_workers=12n8n + Redis + Mongo arbiter + główny dispatch
vps-i16 vCPU8 GBmusi zostać 0 (incydent #2979 — OOM po włączeniu)Monitoring stack
vps-h1niepotwierdzone (dokumentacja przestarzała)niepotwierdzonen/aZamrożony po WAHA — dokumentacja mówi “frozen”, pamięć projektu mówi “wolna pojemność” — rozjazd wymagający weryfikacji

Kluczowa liczba do sizingu — koszt RAM na workera (z 038_ram_budget_dispatch.sql, realnie używane w produkcji): light = 3 GB, heavy/playwright = 6 GB (5 GB na vps-i1), orchestrator = 12 GB, subagent (chmura Anthropic) = 0 GB (celowo wyzerowany — działa po stronie Anthropica, nie obciąża hosta). Wzór: dostępne = (os_ram_gb - reserved_ram_gb) × 0.80 - Σ(zajęte przez running/claimed).

n8n: brak udokumentowanego zapotrzebowania RAM/CPU — tylko Redis ma jawny limit (maxmemory 256mb). Nie zgadywać tej liczby — wymaga pomiaru przed zakupem.

8.2 docs/strategia-p24-infra-2026.md już odpowiada na część pytania

Strategia (2026-06-27, §4) explicite odradza zakup nowego serwera teraz:

  • Obecnie: 6-7 równoległych agentów. Plan dochodzi do 11-12 agentów bez nowego serwera przez uruchomienie bms-3 jako 3. węzła dispatch (3-4 lekkich agentów, ~4GB zapasu po MongoDB SECONDARY + staging).
  • Jawny warunek bramkujący zakup: “Jeśli bms-3 CPU okaże się niewystarczający […] → wtedy zakup nowego serwera. Testować pod obciążeniem przez 2 tygodnie przed decyzją.”
  • Jeśli mimo to trzeba kupić — dwie skonkretyzowane opcje już rozważone:
    • OVH Kimsufi ECO-4: 8 vCPU, 32 GB, ~€60-70/mies.
    • OVH Rise-3: 16 vCPU, 64 GB NVMe, ~€140-160/mies. (“zapas na lata”)
  • Strategia odradza dedykowany serwer GPU — burst AI (Whisper/SDXL/LLM) powinien iść na RunPod Serverless zamiast stałej inwestycji.
  • Żaden dokument nie wspomina 64GB jako konkretnego celu z uzasadnieniem — dwie realne opcje to 32GB/8-core (€65/mies.) i 64GB/16-core (€150/mies.). Opcja 24-core nie ma żadnego precedensu w dokumentacji.

8.3 Kontekst budżetowy

Obecny koszt całkowity ~€474-655/mies. (rozbieżność między dwoma źródłami w pamięci — wymaga odświeżenia). Żaden dokument nie podaje twardego sufitu budżetowego na nowy serwer — jedyne konkretne liczby to dwie opcje OVH powyżej, które podniosłyby całkowity koszt do ~€534-634/mies. w zależności od wyboru.

Kluczowe ustalenia (§8)

  1. Pytanie “ile RAM/rdzeni” ma już częściową odpowiedź w istniejącej strategii — i ta odpowiedź brzmi “jeszcze nie kupuj, dokończ bms-3 najpierw”. Kupowanie nowego serwera teraz pomija tańszą opcję (bms-3 już istnieje, kosztuje 0 dodatkowo, tylko wymaga dokończenia re-provisioningu po nieudanej próbie z 2026-06-30 — patrz §5.3).
  2. Jeśli po 2-tygodniowym teście obciążeniowym bms-3 faktycznie się nasyci, dokumentacja już wskazuje 64GB/16-core (OVH Rise-3, ~€150/mies.) jako racjonalny wybór “na lata” — nie 24 czy 32-core, dla których brak uzasadnienia w danych.
  3. n8n (który ma też chodzić na nowym serwerze wg planu użytkownika) nie ma udokumentowanego realnego zapotrzebowania RAM — to trzeba zmierzyć, nie zgadywać.

Rekomendacje (§8)

  • P0 — Przed jakimkolwiek zakupem: dokończyć re-provisioning bms-3 wg docs/playbooks/provision-bms-3-dispatch-node.md (2-tygodniowy soak test) — to zamyka zarówno §5.3 (redundancja dispatchera) jak i §8 (capacity) jednym działaniem, bez kosztu.
  • P1 — Jeśli bms-3 się nasyci: kupić OVH Rise-3 (16 vCPU / 64 GB NVMe, ~€150/mies.) — to jedyna opcja z realnym uzasadnieniem w dokumentacji (“zapas na lata”), NIE 24-rdzeniowy wariant bez precedensu.
  • P1 — Zmierzyć realne zużycie RAM/CPU n8n (main + worker kontenery) na bms-4 przed przeniesieniem/skalowaniem na nowy serwer — dziś to niewiadoma.
  • P2 — Zweryfikować rozjazd co do specyfikacji vps-h1 (dokumentacja “frozen” vs pamięć projektu “wolna pojemność ogólna”) i zaktualizować docs/environments/vps-h1.md.

§9. Gotowość produkcyjna — 3. licencja Claude dla EcoTrans / MCP w produkcji

9.1 Stan obecny uwierzytelniania (zweryfikowane)

Workery na vps-i1/bms-4 logują się przez OAuth subskrypcji Claude Max, nie per-token API (docs/ai-agent-operations.md §2). Każdy seat = jeden użytkownik Linux (/home/claude-runner*/.claude/.credentials.json, token ~8-12h TTL). Koszt jest płaski per-seat, nie licznikowy. ANTHROPIC_API_KEY istnieje osobno tylko dla n8n/audit-engine i jest jawnie unset przed każdym wywołaniem claude — dwa modele rozliczeń są celowo odseparowane (issue #2144).

CLAUDE_WORKER_ID to tylko etykieta audytowa — nie determinuje, który seat jest używany. Wybór seata dzieje się przez _check_subscription() (spawn-worker.sh), który czyta per-użytkownikowy sub-status.json i blokuje (exit 5) gdy zużycie zbliża się do limitu; dispatcher wtedy round-robinuje na kolejny wolny seat. Nie znaleziono w repozytorium udokumentowanej, przetestowanej liczby “ile równoległych claude -p może obsłużyć jeden seat” — to nieprzetestowana niewiadoma, nie ustalony fakt.

Model pojemności dziś (dev_r_server_capacity, migracje 031/038) liczy wyłącznie RAM/CPU maszyny — nie ma wymiaru “seat/konto”. Śledzenie wyczerpania seatów (claude_accounts JSONB) to osobny, niepodłączony do budżetu RAM mechanizm.

9.2 Co to oznacza dla 3. licencji wchodzącej do produkcji EcoTrans

  1. seat dodaje kolejny niezależny login Linux/OAuth — round-robin dispatchera automatycznie zacznie go używać, ale bez zmian capacity-modelu, obciążenie nadal jest liczone tylko w RAM, nie w “ile requestów na sekundę może znieść dany seat”. Jeśli ten seat ma obsługiwać stałe procesy MCP odpytujące bazę, konwertujące dokumenty, wysyłające do ekstrakcji — to jest inny profil obciążenia niż dzisiejsi workerzy (długie, ale rzadkie sesje autonomicznego kodowania) — bliżej ciągłego strumienia krótkich zapytań.

Rekomendacja architektoniczna: nie mieszać tego z flotą workerów OAuth. Procesy MCP produkcyjne dla EcoTrans (odpytania bazy, konwersje, ekstrakcja) powinny:

  • Mieć własny monitoring (nie dzielony z monitoringiem workerów kolejki) — osobne metryki: liczba zapytań/min, czas odpowiedzi, błędy per typ operacji (DB query / konwersja / wysyłka do ekstrakcji), wpięte w istniejący Prometheus/Grafana stack (nowy exporter albo rozszerzenie queue-exporter).
  • Mieć jawny alert na “proces MCP nie odpowiada / brak heartbeat” — inaczej cichy restart/crash zostanie zauważony dopiero przy reklamacji klienta, nie przez monitoring.
  • Być rozliczane pod tym samym reżimem sekretów co reszta (SOPS, nigdy hardcoded) — to już jest standardem w repo, tylko przypominam żeby nie robić wyjątku “bo produkcja”.

9.3 Architektura workerów jednorazowych (150k tokenów wejścia → 20k wyjścia)

To fundamentalnie inny typ zadania niż dzisiejsi workerzy kodujący — i powinien być budowany inaczej, nie jako kolejny claude -p w tej samej flocie.

Różnica w kształcie obciążenia:

  • Dzisiejszy worker kodujący: długa, autonomiczna sesja z wieloma wywołaniami narzędzi (git, testy, edycje plików), nieprzewidywalny czas trwania, realne zużycie RAM na hoście (klon repo, procesy narzędziowe) — stąd koszt 3-12 GB per slot w budżecie RAM.
  • Zapytanie “zbuduj strategię marki”: jedno, ograniczone w czasie wywołanie generujące completion (150k tokenów kontekstu w, ~20k tokenów wyjścia). Brak pętli narzędziowej, brak klonowania repo, brak dotykania systemu plików poza odczytem promptu i zapisem wyniku. Koszt na hoście to głównie utrzymanie jednego połączenia/procesu w oczekiwaniu na inferencję — realistycznie rząd wielkości mniej RAM niż pełny worker kodujący (setki MB, nie kilka GB), bo nie ma narzutu Claude Code CLI z pełnym tool-use i kontekstem repo.

Znaleziono już zaprojektowany, ale niezbudowany szkielet dokładnie pod ten przypadek: supabase/migrations/048_ai_job_queue.sql (tabele ai_jobs, ai_usage, ai_rate_limits)

  • koncepcja BullMQ z limitami współbieżności per typ zadania opisana w docs/strategia-p24-infra-2026.md §3 (np. timeout 5 min dla “strategy”, 2 min dla “article”, 1 min dla “script”). Kluczowa różnica: ten system rozlicza się przez ANTHROPIC_API_KEY (usage-based), nie przez seat OAuth. infra-src/ai-worker/ nie ma jeszcze żadnych plików — architektonicznie słuszny kształt, ale niezbudowany.

Rekomendacja: dokończyć budowę tego (migracja 048 + infra-src/ai-worker/) jako DEDYKOWANY tor dla zapytań jednorazowych, zamiast rozszerzać flotę OAuth-seat. Powody:

  1. Limity współbieżności API-key są publiczne, przewidywalne i skalują się z wydatkiem (Anthropic automatycznie podnosi tier użycia wraz z historią rozliczeń) — w przeciwieństwie do limitów subskrypcji Max, które w tym repo są nieudokumentowane i dzielone z realną pracą deweloperską/administracyjną na tych samych seatach.
  2. Nie ryzykuje się ubicia limitu seata deweloperskiego zapytaniem klienckim w środku nocy — to by zablokowało realną pracę workerów kodujących (dokładnie ten konflikt, którego dziś unika się przez unset ANTHROPIC_API_KEY przed claude -p).
  3. Koszt per-token jest transparentny i łatwy do wycenienia/rozliczenia klientowi (co będzie potrzebne, gdy EcoTrans zacznie oferować to jako usługę) — flat-fee seat nie daje takiej granulacji.
  4. Sprzętowo: taki tor nie wymaga nowego dedykowanego serwera — RAM per-zapytanie jest na tyle niski, że mieści się w istniejącym budżecie bms-4/vps-h1 z osobnym, małym limitem współbieżności (np. 10-20 równoległych zapytań, do zmierzenia empirycznie po zbudowaniu, nie do zgadnięcia teraz).

Czego nie wiemy i trzeba zmierzyć przed ustaleniem twardej liczby “ile równoległych wątków”: (a) rzeczywisty koszt RAM jednego wywołania API SDK (bez CLI) — prawdopodobnie niski, ale niezmierzony w tym repo; (b) rzeczywisty limit RPM/TPM konta Anthropic API na obecnym tierze rozliczeniowym (zależny od historii wydatków, nie jest stałą liczbą — to trzeba sprawdzić w panelu Anthropic Console, nie zgadywać tutaj).

Kluczowe ustalenia (§9)

  1. Dodanie 3. seata OAuth nie jest samo w sobie gotowe na “stałe procesy MCP w produkcji” — model pojemności dziś liczy tylko RAM, nie ma wymiaru “ile zapytań/min zniesie seat”.
  2. Zapytania klienckie typu “zbuduj strategię marki” to inny kształt obciążenia niż dzisiejsi workerzy — nie powinny dzielić floty OAuth-seat ani jej modelu pojemności.
  3. Istnieje już zaprojektowany (ale niezbudowany) właściwy szkielet dla tego przypadku (migracja 048 + infra-src/ai-worker/) — brakuje dokończenia budowy, nie projektu.

Rekomendacje (§9)

  • P0 — Przed włączeniem stałych procesów MCP na produkcję: dodać dedykowany monitoring (heartbeat, latency, error rate per typ operacji) — inaczej awaria zostanie wykryta przez klienta, nie przez nas. Powiązane z §10 (dashboard klienta).
  • P1 — Dokończyć infra-src/ai-worker/ + migrację 048 jako dedykowany tor dla zapytań jednorazowych (brand-strategy i podobne), rozliczany przez ANTHROPIC_API_KEY, oddzielony od floty OAuth-seat workerów kodujących.
  • P1 — Zmierzyć empirycznie (po zbudowaniu) koszt RAM jednego zapytania SDK oraz rzeczywisty limit RPM/TPM konta API na bieżącym tierze — dopiero to da twardą liczbę “ile równoległych wątków na maszynę”, nie zgadywanie teraz.
  • P2 — Rozszerzyć model pojemności (dev_r_server_capacity) o wymiar “seat/konto”, nie tylko RAM/CPU — dziś seat-owe wyczerpanie (claude_accounts) i budżet RAM to dwa niepołączone mechanizmy.

§10. Specyfikacja dashboardu klienckiego (Portal)

10.1 Co dziś istnieje jako źródło danych

Pipeline e-mail (“DISCO” = AI-Logs, mailgun-v42-prod):

Gmail/klient → Mailgun MX → mailgun-v42-prod (bms-1)
  ├─ upload pliku → s3-v2-v42-prod (Wasabi S3)
  └─ nowy rekord → v42-prod (backend w4) → MongoDB w4_db.regRecords
→ silnik procesowy "aiProcessingLog" (aktywności 296→297→298/300→71/72)

Źródło danych per-wiadomość: MongoDB w4_db.regRecords, pole processStatusId (71 = sukces, 72 = błąd przetwarzania, 300 = kolejka do weryfikacji duplikatu, 298 = potwierdzony brak duplikatu). To jest surowe MongoDB, nie Supabase — brak dziś jakiejkolwiek tabeli agregującej sukces/porażkę.

Pipeline WhatsApp: dwa osobne systemy:

  • Żywy monitoring (docs/whatsapp-group-monitoring.md): WAHA → n8n /wa-router → Supabase whatsapp_messages (status new) → co 5 min AI → p24_l_emails_inbox. Rejestr grup: whatsapp_groups (JID, alias, monitored). To najbliższy istniejący odpowiednik “wiadomość X wyekstrahowana poprawnie/błędnie” — już w Supabase, relacyjne — ale bez żadnego dashboardu, zarządzane dziś przez surowe SQL.
  • Ekstrakcja historyczna (docs/whatsapp-extraction-pipeline.md) — osobny, w większości niezbudowany projekt (5 faz, tylko faza 1 scalona) do importu ZIP-ów eksportu czatu. Nie jest to żywy monitoring, którym klient by się interesował na co dzień.

10.2 Architektura dzisiejszego portalu

portal/ (Next.js, Pages Router): jedna chroniona hasłem strona (dashboard.tsx) pokazująca ping-i zdrowia usług, statyczny link do MkDocs. Auth: Supabase Auth, email/hasło, jeden poziom dostępu — brak ról, brak konceptu ograniczonego/częściowego widoku. To dziś czysto wewnętrzne narzędzie admina, zero multi-tenant.

10.3 Precedens dla ograniczonego dostępu

Brak precedensu dla dostępu zewnętrznego/klienckiego. Istniejące dashboardy Grafana (grafana-pinbox24-ops-dashboard.md, grafana-healthchecks-dashboard.md) kontrolują dostęp wyłącznie na poziomie loginu do Grafany (tylko wewnętrzny zespół), przez jedną wspólną rolę grafana_readonly — nie multi-tenant. RLS jest standardem w Supabase (docs/supabase-operations.md), ale żaden istniejący wzorzec RLS nie jest per-klient/tenant — każda znaleziona polityka to service_role_all albo jedna wspólna rola readonly, nigdy wiersz-per-klient.

10.4 Luki — co trzeba zbudować od zera

  1. Brak modelu auth per-klient — portal ma jeden poziom dostępu; potrzebna nowa rola (np. client_readonly) + scoping wierszy po client_id/office_id, albo custom claim Supabase Auth + polityka RLS (brak precedensu w repo — to będzie pierwszy taki wzorzec).
  2. Brak warstwy agregacji nad MongoDB — dane e-mail żyją w surowym MongoDB, nic dziś nie synchronizuje ani nie zwierciadli liczników sukces/porażka do queryable, RLS-przyjaznej tabeli Supabase. To wymaga nowego joba sync albo API odczytu. 2a. Brak rollupu sukces/porażka dla WhatsAppwhatsapp_messages/p24_l_emails_inbox mają dane per-wiersz, ale brak widoku/materialized view (dzienne liczniki, heartbeat “czy proces żyje”).
  3. Brak sygnału “czy proces działa” w formie konsumowalnej przez klienta — wewnętrzna Grafana to ma (dla adminów), nic nie jest okablowane pod ograniczoną konsumpcję zewnętrzną.
  4. Portal wymaga nowych stron/routingu + scoped klienta Supabase — dziś to jeden płaski panel bez struktury pod widoki oparte na roli.

10.5 Proponowana specyfikacja (na podstawie luk powyżej)

Model danych (nowe, minimalne):

  • Tabela Supabase client_dashboard_metrics (albo widok materializowany), zasilana: (a) codzienną/godzinną synchronizacją z MongoDB regRecords.processStatusId → liczniki sukces/porażka/duplikat per dzień per office_id, (b) rollupem whatsapp_messages status per grupa/dzień.
  • Kolumna client_id/office_id na każdym wierszu + polityka RLS client_id = auth.jwt() ->> 'client_id' — pierwszy w tym repo wzorzec per-tenant, warto udokumentować jako nowy standard w docs/supabase-operations.md skoro go tworzymy.
  • Heartbeat: prosty wiersz “ostatni udany przebieg pipeline’u X o czasie Y” — czerwony/ zielony wskaźnik “czy proces działa”.

UI (nowa sekcja w portal/, albo osobna, lżejsza aplikacja jeśli auth-model client-facing miałby zanieczyścić istniejący admin-only portal):

  • Karta “Email processing” — liczba przetworzonych dziś/tydzień, % sukces, link do nieudanych (bez ujawniania treści maili — tylko metadane).
  • Karta “WhatsApp monitoring” — analogicznie, per monitorowana grupa.
  • Wskaźnik “proces działa” (zielony/czerwony) dla obu pipeline’ów.

Bezpieczeństwo: klient nigdy nie widzi surowych danych MongoDB ani treści wiadomości — tylko agregaty przez RLS-scoped widok. To jest zgodne z zasadą “żadnych sekretów/danych wrażliwych w warstwie ekspozycji klienckiej”.

Kluczowe ustalenia (§10)

  1. Dane źródłowe istnieją, ale w dwóch różnych, niepołączonych systemach (MongoDB dla e-mail, Supabase dla WhatsApp) — potrzebna warstwa agregacji, nie tylko UI.
  2. Portal dziś nie ma ŻADNEGO konceptu multi-tenant — to od zera, nie rozszerzenie.
  3. To będzie pierwszy wzorzec per-klient RLS w tym repo — warto to od razu udokumentować jako nowy standard, bo prawdopodobnie będzie potrzebny ponownie dla kolejnych klientów.

Rekomendacje (§10)

  • P1 — Zbudować client_dashboard_metrics (sync z MongoDB + rollup WhatsApp) jako pierwszy krok — bez tego UI nie ma czego pokazywać.
  • P1 — Zaprojektować i udokumentować wzorzec RLS per-klient jako nowy standard w docs/supabase-operations.md, nie ad-hoc rozwiązanie tylko dla tego jednego dashboardu.
  • P2 — Zdecydować: rozszerzenie portal/ (Pages Router, dziś admin-only) czy osobna, lżejsza aplikacja client-facing — rozszerzenie istniejącego admin-portalu o auth klienta niesie ryzyko przypadkowego wycieku wewnętrznych danych, jeśli routing/RLS nie będzie precyzyjny.
  • P2 — Powiązać z §9: heartbeat/monitoring procesów MCP produkcyjnych powinien zasilać ten sam dashboard klienta, nie osobny wewnętrzny.

§11. Synteza słabości organizacyjnych i procesowych

Ta sekcja zbiera wzorce przekrojowe widoczne w więcej niż jednej sekcji tego audytu — to są prawdziwe słabości organizacyjne/procesowe, nie pojedyncze bugi.

11.1 Wzorzec #1 — “nowe obok, nie do, istniejącej struktury”

Widoczny w czterech niezależnych miejscach:

  • System ról: 4 równoległe definicje (§4.1) — każda kolejna rola/system dopisywana od zera zamiast do istniejącego rejestru.
  • Dokumentacja dispatchera: worker-queue-operations.md vs queue-dispatcher-priority.md sprzeczne (§5.3) — nowsza prawda w playbooku, starsza (błędna) w ops-doc, nikt nie zaktualizował drugiego.
  • Hub nawigacyjny: docs/index.md nie linkował do najważniejszych dokumentów strategicznych (§3.1, już naprawione w ramach tego audytu).
  • Incydenty: 5+ mechanizmów zapisu, każdy zbudowany dla innego źródła alertu, żaden nie wie o pozostałych (§7.1).

To jest najważniejsza słabość procesowa całego audytu. Nie brakuje dyscypliny pisania dokumentacji (repo ma 353+ dokumentów) — brakuje kroku “zaktualizuj/zlinkuj z istniejącym” przy tworzeniu nowej rzeczy. Rekomendacja przekrojowa w §12.

11.2 Wzorzec #2 — redundancja “na papierze”, nieprzetestowana w praktyce

  • bms-3 jako 3. węzeł dispatch: udokumentowany playbook prowizji, ale węzeł nie działa od 2026-06-30 (§5.3, §8.2).
  • dev-laptop jako 4. węzeł: istnieje w dev_r_server_capacity, tunel niezweryfikowany od 2026-07-08 (§5.3).
  • vps-i1 jako emergency fallback dispatch: 1 slot, w praktyce zarezerwowany za monitoringiem, a nc-alert-batch hard-afinowany wyłącznie tam bez fallbacku (§5.3, §6.3).

Konsekwencja: bms-4 jest de facto jedynym realnym punktem przepustowości dla dev/orchestrator i jedynym realnym miejscem inwestygacji alertów infra — mimo że dokumentacja/schemat sugerują 3-4-węzłową flotę.

11.3 Wzorzec #3 — mechanizmy “cichej degradacji” (fail bez powiadomienia)

  • cancel_unroutable_nc_alert_batch() cicho anuluje zadania po 15 min, bez komentarza/ Discorda (§6.3) — dokładnie ten sam kształt co…
  • …historyczne awarie dispatchera, gdzie rozjazd schematu SQL/Python/bash cicho maskował błędy zamiast crashować (§5.2, 3 z 6 przeanalizowanych playbooków).
  • Sekrety wyciekające przez udaną komendę (nie błąd) nie mają dziś żadnej warstwy wykrycia po fakcie (§7.2).

Wspólny mianownik: system jest zaprojektowany żeby crashować głośno na błędach, ale nie ma warstwy wykrywającej “cichy sukces, który jest de facto błędem” — zarówno w dispatcherze, jak i w bezpieczeństwie sekretów.

11.4 Wzorzec #4 — oczekiwania SLA/procesowe nigdzie nieudokumentowane jako liczby

  • “2-3 min SLA” dla alertów — nigdy nie było zapisane jako formalny target vs. rzeczywistość (raz dziennie) — stąd frustracja użytkownika (§6.2).
  • Ile równoległych workerów na seat Claude Max — nieudokumentowana, nieprzetestowana liczba (§9.1).
  • RAM per worker n8n — nieudokumentowana (§8.1).

Wzorzec: kluczowe liczby operacyjne (SLA, limity współbieżności, zużycie zasobów) żyją jako niepisane założenia w głowach, nie jako zapisane, zweryfikowane fakty. To bezpośrednio prowadzi do nieporozumień typu to, które zgłosił użytkownik w trakcie tej sesji.

Kluczowe ustalenia (§11)

  1. Główna słabość to nie brak narzędzi czy dokumentacji — to brak pętli zwrotnej utrzymującej istniejące rejestry/huby w synchronizacji z nową pracą.
  2. Redundancja infrastrukturalna jest w dużej mierze iluzoryczna — jeden realny host (bms-4) dźwiga większość przepływu.
  3. System dobrze wykrywa głośne awarie, słabo wykrywa ciche/częściowe.
  4. Kluczowe liczby operacyjne (SLA, limity, zużycie zasobów) nie są nigdzie zapisane jako zweryfikowane fakty — co prowadzi do rozbieżnych oczekiwań między człowiekiem a systemem.

§12. Rekomendacje i roadmapa priorytetowa

Zebrane ze wszystkich sekcji, w kolejności priorytetu. ✅ = już wykonane w ramach tego audytu.

P0 — zrobić najpierw (dni, nie tygodnie)

#DziałanieSekcjaStatus
1Skorygować udokumentowany model SLA alertów: instant-dispatch = komentarz inwestygacyjny (2-15 min), naprawa = raz dziennie (20:00 UTC) — nie 2-3 min§6.2
2cancel_unroutable_nc_alert_batch() — przestać cicho anulować, dodać komentarz GH + Discord§6.3
3Potwierdzić, czy maskowanie HTTP 5xx w nc-alert-instant-dispatch.yml jest naprawione (playbook mówił “PR pending”)§5.5
4Zunifikować worker-queue-operations.md z realnym stanem dev_r_server_capacity§5.3
5Scalić 4 systemy ról → jedno źródło prawdy w docs/roles/<rola>/persona.md§4.3
6Nadać p24-network-admin brakujący skill /role-p24-network-admin§4.3
7Rozszerzyć agent_errors/incident log o kolumny reporter/machine/comment (NOT NULL)§7.3
8Rozszerzyć hook bezpieczeństwa o skan niezależny od exit-code + nowy PostToolUse na Read§7.3
9Dokończyć re-provisioning bms-3 (2-tyg. soak test) PRZED zakupem nowego serwera§5.3, §8.3
10Dopisać linki do analiza-infrastruktury.md/strategia-p24-infra-2026.md/docs/audits/INDEX.md w docs/index.md§3.3zrobione w tej sesji
11Dodać monitoring/heartbeat dla procesów MCP produkcyjnych EcoTrans PRZED włączeniem na produkcję§9.2

P1 — kolejne 2-4 tygodnie

#DziałanieSekcja
12CI/pre-merge gate wykrywający rozjazd kolumn schema (bash/Python/SQL) po migracjach dev_r_*§5.5
13Dodać bms-4 jako fallback dla nc-alert-batch (dostęp do Prometheusa)§6.4, §5.3
14Rozwiązać los systemu ról C (.claude/roles/ / /open-role) — scalić lub jawnie zdeprecjonować§4.3
15Rozszerzyć credential-mapping workerów zdalnych o sys-security i dev-tester§4.3
16Zbudować widoki generujące secrets-rotation-log.md/incident-register.md z jednej tabeli§7.3
17Dodać session_id jako wspólny klucz między tabelami incydentów§7.3
18Jeśli bms-3 się nasyci: kupić OVH Rise-3 (16 vCPU/64GB, ~€150/mies.) — NIE 24-core§8.3
19Zmierzyć realne zużycie RAM/CPU n8n na bms-4§8.3
20Dokończyć infra-src/ai-worker/ + migrację 048 jako dedykowany tor dla zapytań jednorazowych (brand-strategy), rozliczany przez API key, nie seat OAuth§9.3
21Zmierzyć empirycznie koszt RAM + limit RPM/TPM API po zbudowaniu — dopiero to da liczbę równoległych wątków§9.3
22Zbudować client_dashboard_metrics (sync MongoDB + rollup WhatsApp) jako fundament dashboardu klienta§10.5
23Zaprojektować + udokumentować pierwszy wzorzec RLS per-klient jako nowy standard§10.5

P2 — w tle, gdy jest czas

#DziałanieSekcja
24Dedykowany alert “oczekiwany węzeł dispatch nieobecny >X h”§5.5
25Naprawić nieaktualny tekst “co godzinę” w nightly-devops-triage.md§6.4
26Ujednolicić PR-y sesyjne (rotation logi) do używania Closes #N konsekwentnie§5.6
27Rozważyć krótki dashboard czytający ujednoliconą tabelę incydentów§7.3
28Zweryfikować rozjazd specyfikacji vps-h1 (frozen vs wolna pojemność)§8.3
29Rozszerzyć dev_r_server_capacity o wymiar seat/konto, nie tylko RAM/CPU§9.3
30Decyzja: rozszerzyć portal/ czy zbudować osobną app client-facing§10.5
31Dać dodatkowy śróddzienny przebieg nightly-devops-triage tylko dla nc-alert§6.4
32Indeks tematyczny dla 241 playbooków (analogiczny do docs/audits/INDEX.md)§3.3

Sekwencja zależności (co blokuje co)

P0#9 (bms-3 soak test) ──→ P1#18 (decyzja o zakupie HW)
P0#5 (scalenie ról)     ──→ P1#14 (decyzja o systemie C)
P0#7+8 (schema incydentów) ──→ P1#16+17 (widoki + session_id)
P1#20 (ai-worker build)  ──→ P1#21 (pomiar RAM/limitów)
P1#22 (dashboard metrics) ──→ P1#23 (RLS pattern) ──→ P2#30 (portal decision)

§13. Załączniki

Metodologia zbierania danych

Ten audyt użył 8 równoległych agentów badawczych (Agent tool, subagent_type: general-purpose, tryb tła), każdy z samodzielnym, w pełni kontekstowym promptem (bez dostępu do reszty tej rozmowy) i jawnym zakazem wyświetlania wartości sekretów. Zakresy:

  1. Dispatcher/kolejka workerów — architektura, katalog awarii, wąskie gardła (§5)
  2. System ról Claude — mapa 4 systemów, pokrycie per rola (§4)
  3. SLA alertowania — pełny łańcuch przyczynowy (§6)
  4. Bieżący stan zgłaszania incydentów — mapa 5+ mechanizmów (§7.1)
  5. Dane do capacity planning — specyfikacje serwerów, koszt RAM/workera (§8)
  6. Pipeline monitoringu maili/WhatsApp — źródła danych pod dashboard klienta (§10)
  7. Weryfikacja auto-close issues po merge PR (§5.6)
  8. Architektura workerów jednorazowych + model uwierzytelniania Claude Max (§9)

Każdy agent pracował wyłącznie na plikach repo (Read/Grep/Glob/Bash read-only) — brak modyfikacji, brak wykonania destrukcyjnych komend. Pełne transkrypty dostępne w logach sesji (nie kopiowane tutaj — powyższe sekcje to zweryfikowana synteza, nie surowy dump).

Dokumenty źródłowe, na których zbudowano ten audyt

  • docs/analiza-infrastruktury.md (2026-06-27) — mapa procedur, priorytetów, automatyki
  • docs/strategia-p24-infra-2026.md (2026-06-27) — strategia rozwoju, capacity plan
  • docs/priorities.md — historia 71 sesji, aktywne ryzyka P0
  • docs/index.md — 18-perspektywowa taksonomia (zaktualizowana w tej sesji, §3.2)
  • docs/audits/2026-07-02-secrets-documentation-audit.md — poprzedni audyt dokumentacji
  • 241 playbooków w docs/playbooks/
  • Kod źródłowy: infra-src/meta-dispatcher/, scripts/queue-dispatcher*.py/.sh, scripts/spawn-worker.sh, migracje Supabase (monitoring/supabase/migrations/)

Co NIE weszło w zakres tego audytu (świadomie pominięte)

  • Pełny przegląd bezpieczeństwa (osobny proces: /security-review, poza zakresem zleconym)
  • Szczegółowy audyt kosztów SaaS per-usługa (odsyła do docs/12-costs/README.md)
  • Compliance EU AI Act (osobny, już istniejący dokument: docs/eu-ai-act-compliance.md)
  • Implementacja rekomendacji — ten dokument to audyt + plan, nie PR z gotowym kodem; wdrożenie poszczególnych P0/P1/P2 z §12 to osobne zadania/issues.

§14. Higiena równoległości sesji + stacja dewelopera jako węzeł mocy

Pytania użytkownika dopisane w toku sesji: (1) jak radzić sobie z równoległością sesji, żeby nie mieszać branchy; (2) jak odnotowywać sesje lokalne wykonywane bezpośrednio na komputerze deweloperskim jako pełnoprawne workery/agenty w kolejce; (3) jak rozszerzyć pulę workerów o komputer deweloperski — rejestracja zasobów, priorytet nocny dla ciężkich zadań (dużo RAM, w nocy bezczynny).

14.1 Mieszanie branchy między sesjami — w większości już rozwiązane

Zweryfikowano: dokładnie ten problem wystąpił i został naprawiony tego samego dnia (docs/playbooks/shared-checkout-worktree-isolation.md, issue #3230, 2026-07-08). Dwa stray commity z niepowiązanych sesji wylądowały na branchu tej orkiestrującej sesji w ciągu jednej sesji roboczej — bo wiele równoległych sesji (główna sesja, spawnowane Agent(), inne sesje ludzkie/agentowe na tym samym komputerze) operowały na tym samym primary checkout (C:\code_2026\p24-infra) zamiast izolowanych worktree’ów wymaganych przez CLAUDE.md.

Naprawa techniczna już wdrożona: hooki pre-bash-safety.sh/pre-bash-safety-windows.ps1 teraz blokują git commit/git checkout -b/git switch -c/git branch -m uruchomione w primary checkout (wykrywane przez --git-dir == --git-common-dir) — działa tylko wewnątrz tmp/wt-{issue}. To dokładnie ten mechanizm, o który pyta użytkownik — już istnieje i jest technicznie wymuszany, nie tylko udokumentowany.

Znane, jawnie udokumentowane ograniczenia (z samego playbooka):

  • Chroni tylko sesje przechodzące przez system hooków Claude Code na tej maszynie — nie chroni przed ręcznymi komendami git poza Claude Code, ani przed innymi maszynami/CI operującymi na klonie repo.
  • Nie cofa szkód sprzed 2026-07-08 — tylko zapobiega nowym przypadkom.

Rekomendacja: ten mechanizm jest solidny i wystarczający dla obecnego pytania — nie trzeba nic dodatkowo budować, tylko upewnić się, że każda nowa sesja (w tym ta obecna) faktycznie zaczyna pracę przez git worktree add, a nie bezpośrednio w primary checkout. Jedyna realna luka: rozszerzyć tę samą logikę wykrywania na workery zdalne (bms-4/vps-i1), jeśli tam też zdarzają się równoległe sesje na tym samym klonie — dziś ochrona jest zweryfikowana tylko dla tej maszyny Windows.

14.2 Śledzenie sesji lokalnych jako pełnoprawnych workerów — też już istnieje

Tabela dev_r_agent_sessions (Supabase) + skrypty agent-session-start.py/ agent-session-end.py już implementują dokładnie to, o co pyta użytkownik — i już są podłączone jako hooki na tej maszynie: zweryfikowano w .claude/settings.json: SessionStartagent-session-start.sh, Stopagent-session-end.sh (completed/failed). Schemat już rozróżnia type='worker' (sesja główna) vs type='subagent' (spawnowany Agent()), z hierarchią parent_session_id. Mapowanie P24_WORKER_LABEL=local, P24_ENVIRONMENT=local jest już udokumentowane dla sesji na komputerze deweloperskim (docs/agent-session-audit-operations.md).

Co to oznacza praktycznie: ta właśnie sesja, w której piszesz ten audyt, już powinna być widoczna w dev_r_agent_sessions z environment=local. To realnie odpowiada na pytanie “czy to też jest spełniony worker/agent, który powinniśmy odnotować” — tak, i już jest odnotowywany, o ile zmienne środowiskowe P24_WORKER_LABEL/P24_ENVIRONMENT są faktycznie ustawione w profilu PowerShell na tej maszynie (do zweryfikowania — patrz rekomendacja P1 poniżej).

Rekomendacja: zweryfikować (nie zakładać), że $PROFILE PowerShell na tym komputerze faktycznie eksportuje P24_WORKER_LABEL=local/P24_ENVIRONMENT=local zgodnie z tabelą w docs/agent-session-audit-operations.md — jeśli nie, sesje lokalne piszą się do tabeli z pustymi/domyślnymi etykietami, co utrudnia filtrowanie w Grafanie.

14.3 Rozszerzenie floty workerów o komputer deweloperski — nowa architektura

To jest jedyna część tego pytania, która wymaga realnego nowego projektowania — dziś dev_r_server_capacity nie ma wiersza dla tej maszyny (windows-dev), w przeciwieństwie do dev-laptop, które już przeszło dokładnie tę samą transformację (playbook docs/playbooks/dev-laptop-claude-remote.md + dev-laptop-claude-remote.md §Hardware — 8 GB RAM, Ubuntu 24.04, autossh reverse tunnel do vps-i1, claude_accounts/sub-status.json per-seat). To jest gotowy wzorzec do powielenia, nie coś do wynajdowania od zera.

Kluczowa przeszkoda techniczna: dispatcher dziś działa w modelu push przez SSH (queue-dispatcher-loop.py SSH-uje na hosta i uruchamia spawn-worker.sh przez systemd-run w cgroup). To wymaga (a) serwera SSH na docelowej maszynie, (b) systemd i basha. Windows nie ma tego natywnie. dev-laptop obszedł ten problem, bo to Linux (Ubuntu). Dla tej maszyny (Windows 10) realne opcje:

  1. WSL2 z Ubuntu (rekomendowane) — zainstalować dokładnie ten sam stack co dev-laptop (claude-runner user, OAuth Claude Max, sub-status.json) wewnątrz WSL2, wystawić SSH server wewnątrz WSL2 (albo przez port-forward Windows→WSL2), i podłączyć tunelem autossh do vps-i1 dokładnie jak dev-laptop. Dispatcher widzi to jako kolejny host Linux — zero zmian w queue-dispatcher-loop.py.
  2. Model pull zamiast push (alternatywa, większy koszt) — lekki proces (PowerShell albo Python) na Windows, który sam odpytuje kolejkę Supabase o dostępne zadania, gdy komputer jest bezczynny, i sam odpala claude -p. To wymagałoby nowej logiki dispatchera (dziś on nigdy nie jest “odpytywany”, zawsze “pcha”) — więcej pracy, mniej przetestowany wzorzec.

Rekomendacja: opcja 1 (WSL2), bo powiela w 100% już sprawdzony, działający wzorzec dev-laptop — najmniejsze ryzyko, najmniejszy koszt wdrożenia.

Rejestracja i priorytet nocny — schemat już to wspiera:

  • dev_r_server_capacity ma już kolumny last_heartbeat/server_status (migracja 049, healthy <5min / degraded <15min / down >15min) — dokładnie mechanizm “zarejestruje się jako działający” o który prosi użytkownik. Nowy wiersz server_label='windows-dev' z os_ram_gb/os_vcpus (realna specyfikacja tej maszyny — do podania, nie zgadywania) i heartbeatem z WSL2 wystarczy.
  • max_weight_prime/max_weight_night (migracja 040) już implementują dokładnie “priorytet nocny dla ciężkich” — wystarczy ustawić max_weight_night='heavy' (albo nawet orchestrator, jeśli RAM na to pozwala) i max_weight_prime='light' (albo enabled=false w godzinach dziennych, jeśli użytkownik nie chce ŻADNEJ interferencji z pracą interaktywną w dzień).

Ryzyko, którego użytkownik nie wspomniał, ale trzeba zaadresować: dispatcher nie wie “czy użytkownik właśnie pracuje interaktywnie na tym samym komputerze” — bez dodatkowego sygnału, ciężki job w tle mógłby zjeść RAM/CPU w trakcie, gdy deweloper normalnie używa maszyny (nawet w dzień, jeśli max_weight_prime nie jest light/disabled). Rekomendacja: dodać prosty sygnał “aktywna sesja interaktywna” (np. plik-znacznik pisany przez SessionStart/Stop hook z §14.2 — te hooki już istnieją!) i bramkować dispatch nocny/dzienny na tej maszynie o ten sygnał, nie tylko o porę dnia.

Kluczowe ustalenia (§14)

  1. Dwa z trzech pytań użytkownika mają już gotowe, działające rozwiązania w repo — wymagają weryfikacji/dokończenia, nie budowy od zera.
  2. Trzecie (rozszerzenie floty o komputer dewelopera) ma gotowy wzorzec do powielenia (dev-laptop) i gotowy schemat capacity (max_weight_night, heartbeat) — brakuje tylko samego wdrożenia (WSL2 + rejestracja).
  3. Nieoczekiwane ryzyko: dispatcher musi wiedzieć, kiedy użytkownik pracuje interaktywnie na tej samej maszynie, żeby nie konkurować o zasoby — da się to rozwiązać tymi samymi hookami, które już śledzą sesje (§14.2).

Rekomendacje (§14)

  • P1 — Zweryfikować że $PROFILE na tej maszynie eksportuje P24_WORKER_LABEL=local/ P24_ENVIRONMENT=local (dokończenie §14.2, koszt: 5 minut).
  • P1 — Zainstalować WSL2 Ubuntu na tej maszynie, powielić stack dev-laptop (claude-runner, autossh tunnel do vps-i1, sub-status.json) — jeden dzień pracy wg istniejącego playbooka.
  • P1 — Dodać wiersz windows-dev do dev_r_server_capacity z realną specyfikacją RAM/CPU tej maszyny, max_weight_night='heavy', max_weight_prime='light' (do ustalenia z użytkownikiem czy w ogóle chce dispatch w dzień).
  • P1 — Dodać bramkę “aktywna sesja interaktywna” (plik-znacznik z hooków SessionStart/Stop) żeby dispatcher nie konkurował o RAM z realną pracą deweloperską.
  • P2 — Rozszerzyć wykrywanie mieszania branchy (§14.1) na workery zdalne, jeśli tam też zdarzają się równoległe sesje na tym samym klonie.

§15. Pinbox24 — stabilność redeploymentu, Angular vs Next.js, środowisko staging

Pytania użytkownika dopisane w toku sesji: (1) jak stabilnie opracowany jest dziś redeploy Pinbox24; (2) dwie opcje rozwoju — przepisać na Next.js jako multi-tenant dla ~7 klientów (porzucając starą aplikację Angular) vs zostać przy Angularze, ale wtedy sprawdzić bezpieczeństwo kontenerów i co trzeba podnieść wersyjnie; (3) w pełni działające środowisko staging do testowania podnoszenia wersji przed produkcją — osobny serwer czy bms-1?

15.1 Stabilność redeploymentu — dziś fragile, dopiero od 2-3 dni w drodze do repeatable

Katalog konkretnych przyczyn awarii przy redeployu (z 6+ postmortemów w docs/pinbox24/):

AwariaKlasa przyczynyIle razy się powtórzyła
Zahardkodowane klucze Wasabi w skompilowanym JS — rotacja usuwa klucz, obraz nadal się do niego odwołujekonfiguracja wypieczona w obrazie, ignoruje zmienne env≥3× (w4-download 07-01, w4-pm2-crash 07-06, w4-signed-url 07-08)
Hotfix w kontenerze ginie po docker restart/--force-recreatehotfix aplikowany tylko przez docker exec/PM2 reload, nigdy w obrazie ani bind-mouncie≥3 potwierdzone postmortemy, w tym jeden przypadek gdzie agent rotacji sam skasował hotfix w trakcie tego samego incydentu
v32-prod nie na prod-v-3-net w docker-compose.yml — podpinane ręcznie, znika po recreatekonfiguracja sieci niepersystowana w composeOtwarte, nienaprawione — issue #2826
Zmienne env rozdzielone między dwoma kontenerami (backend-environment.env vs s3-environment.env) — jeden zaktualizowany, drugi nieluka propagacji credentiali/env między powiązanymi usługamiGłówna przyczyna crash-loop v42-prod (506 restartów, 07-01)
docker commit na kontenerze debugowym startowanym przez sleep wypieka zły ENTRYPOINT do obrazuręczny skrót zamiast prawdziwego docker build1× potwierdzone, przywołane jako lekcja
Pliki-łatki (hand-edited, bez kontroli wersji, czasem z zahardkodowanymi credentialami) istniały tylko jako stan serwerapatche nigdy w git, brak kroku CI je wdrażającegoTo był stan DOMYŚLNY aż do dosłownie dziś (2026-07-08, PR #3248 dla w4 + analogiczny follow-up dla w3)

Czy istnieje automatyczny pipeline deployu? Zdecydowanie nie — w większości ręczny SSH+docker.

  • CI/CD na poziomie aplikacji jest martwe: GitLab runner zwraca forbidden (przestarzały/skasowany token, binarka runnera v13.8.0 zbyt stara żeby się zautoryzować) — stąd istnieje osobny playbook ręcznego deployu (docs/playbooks/pinbox24-bms1-manual-deploy.md), bo “GitLab CI broken.”
  • docs/playbooks/pinbox24-ms-deploy.md jawnie oznacza się jako “Human-execution required” — shell script ./docker-deploy-prod.sh, nie automatyczny etap pipeline’u.
  • Jedyne co jest zautomatyzowane to secrets-sync.yml (env + od dziś persistent-patches) — nie dotyka kodu/obrazów aplikacji w ogóle.

Gdzie hotfixy żyją trwale vs gdzie giną:

  • Tylko w pamięci kontenera (ginie przy każdym recreate) — domyślny wzorzec do 2026-07-01, jawnie nazwany w postmortemach jako non-durable.
  • Trwale przez bind-mount + git — wzorzec z fixu mailgun/s3-v2 (2026-07-02), nie był normą w momencie powstania, dopiero w ostatnich 24-48h (PR #3248, 2026-07-08) rozszerzony na kolejne pliki w4 i w3.
  • Wciąż niekonwertowane: excel_import.js (w3), docker-deploy-prod.sh (zahardkodowane credentiale, issue #3063), hasło Mongo w logach PM2 (issue #3060).

    Aktualizacja #4708 (2026-07-30): excel_import.js skreślone — to nie jest plik-łatka. Weryfikacja read-only na bms-1 wykazała, że jest to stockowy, śledzony przez git plik w pinbox24/p24-v-3.2 (tracked + clean, ostatni commit upstream 2020-02-01), bajt-identyczny z kopią wbudowaną w obraz v32-prod (mount to redundantny no-op). Nie wymaga konwersji na bind-mount+git — GitLab jest już źródłem prawdy. Pozostają tylko #3063 i #3060.

Ocena wprost: Pinbox24 redeploy jest dziś kruchy, i dopiero od 2-3 dni w drodze do powtarzalności — nie “stabilnie opracowany”. System przechodzi z “czystego hotfiksowania z wiedzy plemiennej” do “część fixów jest trwała”, ale wiele znanych instancji tego samego ryzyka pozostaje otwartych.

15.2 Bezpieczeństwo kontenerów bms-1 — EOL systemu operacyjnego to blokada niezależna od wyboru frameworka

Stan udokumentowany:

  • Ubuntu 20.04.1 LTS, EOL od kwietnia 2025 — potwierdzone jako CRITICAL/P1 w trzech niezależnych miejscach. Brak jakichkolwiek patchy kernela/pakietów od momentu EOL — to problem “brak dostępnych patchy”, nie zaniedbania.
  • Firewall: UFW aktywny, default-deny, tylko 22/80/443 publicznie. Realna luka znaleziona i naprawiona 2026-06-18: Docker omijał UFW przez łańcuch FORWARD, zostawiając porty 49100-65535 (w tym Portainer) dostępne z internetu — naprawione przez reguły DOCKER-USER.
  • docs/cloudflare-security-operations.md i docs/ssh-hardening-operations.md nie wspominają bms-1/Pinbox24 ani razu — brak udokumentowanego WAF specyficznego dla publicznych endpointów w3/w4 poza standardowym Cloudflare DNS proxy.

Już przeanalizowane, gotowy runbook (nie trzeba wymyślać na nowo):

  • Upgrade do Ubuntu 22.04 — pełny runbook w docs/playbooks/bms1-ubuntu-2204-upgrade.md (issue #1683), jawnie oznaczony jako wymagający wykonania przez człowieka (in-place do-release-upgrade, wymaga KVM OVH + okna serwisowego — “agent Claude nie może i nie powinien tego robić autonomicznie”).
  • docs/bms-1-v3x-sunset-migration-plan.md — plan wygaszenia starego stacku v3.x, ale zablokowany na decyzji biznesowej (identyfikacja 2-3 pozostałych klientów v3.x).

Prawdziwe luki — nieudokumentowane nigdzie w p24-infra:

  • Brak skanowania CVE obrazów Pinbox24 w ogóle. Jedyna propozycja skanowania (docs/improvements/08-image-cve-scanning.md, status proposed, P2) dotyczy wyłącznie stacku monitoringu — nie wspomina obrazów Pinbox24/bms-1.
  • docs/update-management.md ma zero harmonogramu patchowania dla bms-1 — cały miesięczny checklist dotyczy tylko vps-i1.
  • Zawartość obrazów Docker na poziomie aplikacji jest niewidoczna z tego repo — compose files referencjonują gotowe obrazy ECR bez Dockerfile, bez tagu base-image, bez dyrektywy user: — czy kontenery działają jako root jest nieznane z samego p24-infra; realne Dockerfile/wersje Node żyją w osobnych repo GitLab.
  • Jeden wyjątek znaleziony: docs/pinbox24/strategic-decisions-2026-07.md stwierdza, że frontend W3 działa na Angular 7 z “zepsutym driverem MongoDB”, nazywając to “tykającą bombą” — ale to notatka biznesowa, nie skan bezpieczeństwa; żaden audyt zależności/CVE tej wersji frameworka nie istnieje nigdzie w tym repo.
  • Dwa dodatkowe P3: wkhtml-v42-prod na obrazie z ~2015 roku, floating tag, bez pinningu, bez skanowania. Plus dwa otwarte bugi kruchości redeployu (#2826, #3096 — patrz §15.1), które podważyłyby zaufanie do jakiegokolwiek workflow promocji nawet po naprawieniu bezpieczeństwa.

15.3 Angular vs Next.js — decyzja nadal NIE jest podjęta, mimo nazwy dokumentu “strategic-decisions”

Najważniejsze ustalenie tej podsekcji. Trzy dokumenty rozłożone na tydzień (docs/pinbox24/strategic-decisions-2026-07.md 07-01, docs/pinbox24-sunset-vs-supabase-migration.md i docs/pinbox24-supabase-migration-roi.md oba 07-08) dają trzy różne, prowizoryczne kierunki, każdy warunkowany danymi biznesowymi (realny MRR, udział przychodu RESO, trajektoria wzrostu), których żaden z dokumentów faktycznie nie dostarcza:

  1. strategic-decisions-2026-07.md (07-01): rekomenduje przepisać TYLKO W3 (RESO + Gratus — 2 klientów na zepsutym/EOL stacku) jako dedykowaną appkę Next.js, zostawić W4 bez zmian (Keller, ZPK, MOW Malbork, Bibus Menos, Zhonghua, Valmont — stabilne od 18 miesięcy) poza 1-tygodniowym przeniesieniem frontendu Angular→Vercel. Model danych: Supabase (Postgres+JSONB), nie MongoDB ani hybrydowy JSONB. Opcja D (jeden Next.js SaaS dla W3+W4 razem) rozważona, ale jawnie odłożona — nie odrzucona.
  2. pinbox24-sunset-vs-supabase-migration.md (07-08): krótka odpowiedź ”❌ NIE jeśli faktycznie wygaszamy… ✅ ale jeśli zatrzymujemy klientów, Supabase bije modernizację legacy.”
  3. pinbox24-supabase-migration-roi.md (07-08): status zmieniony z “sunset” na “migrate” po odkryciu że bms-2/3/4 (€52-70/mies.) mogłyby zostać wyłączone — ale własna matematyka pokazuje naiwny zwrot ~8-10 lat, “poprawiony” tylko przez spekulatywne scenariusze wzrostu klientów.

Rekonsyliacja liczby klientów — “7” nie trzyma się dokładnie: Per docs/pinbox24/system-analysis.md (żywe zapytanie MongoDB, 07-01): W4 ma 7 aktywnych biur (w tym 3 w regresie/odchodzące: Bibus Menos, Zhonghua, Valmont), W3 ma 2 (RESO EUROPA ~86% ruchu W3, Gratus). To daje 9 realnych aktywnych klientów, ale strategic-decisions-2026-07.md jawnie wyklucza Eco-Trans z listy W4 (traktując go jako wdrożenie wewnętrzne, nie płacącego klienta) → 8 zewnętrznych klientów. Liczba “7” użyta w obu dokumentach z 07-08 to zaokrąglenie niezweryfikowane wobec faktycznej listy z system-analysis.md.

Kluczowe: Pinbox24 jest już częściowo multi-tenant na poziomie danych. Nazwy v31-v42 to wersje dwóch współdzielonych baz kodu (W3, W4), nie osobny stack per klient — w3_db/w4_db już dzielą wspólny katalog rejestrów/formularzy między klientami przez officeId. To istotnie zmienia ramę “przepisz żeby uzyskać multi-tenancy” — multi-tenancy na poziomie danych już istnieje, brakuje jej na poziomie frontendu/deploymentu.

Liczby kosztowe, które już istnieją (nie trzeba szacować od zera):

  • Pełny rewrite (greenfield, Supabase, skala RESO): 12-14 tyg., €30-50k, ryzyko WYSOKIE.
  • Lift & shift (Mongo→Supabase, istniejąca logika frontendu): 5-6 tyg., €15-20k, ryzyko ŚREDNIE.
  • Faza 1 (tylko RESO, proof-of-concept Next.js/React+Vercel+Supabase): 6 tyg., €20k.
  • Osobna, bardziej konkretna analiza kodu (pinbox24-w3-w4-architecture-spec.md, audyt kodu 07-06) oceniła migrację obecnego kodu Angular/Node na Vercel (nie rewrite): W3 niewykonalne bez 4-8 tyg. przepisania (klaster PM2, trwałe patche); W4 niewykonalne bez 8-12 tyg. (Socket.IO, RabbitMQ, lokalny Redis, dynamiczny cron, 7 współlokowanych mikroserwisów) — werdykt: “Oba zostają na bms-1.”

Czy et-operational-platform/et-lager to wyznaczony następca Next.js? Nie — nigdzie tak nie wyznaczono. Wspomniane raz, jako nierozwiązane pytanie otwarte w pinbox24-supabase-migration-roi.md — nigdy nieodpowiedziane w żadnym dokumencie. To osobny produkt (wewnętrzne zarządzanie flotą Eco-Trans) dla innej funkcji niż Pinbox24 (rejestr dokumentów/workflow dla zewnętrznych klientów B2B). Rewrite Pinbox24 na Next.js byłby faktycznie osobną budową — nie ma istniejącego kodu Next.js gotowego to wchłonąć.

Ocena wprost (synteza dla użytkownika): Decyzja “przepisać czy zostać” nie jest podjęta, mimo nazwy pliku. Rekomendacja z najbardziej szczegółowej analizy kodu (architecture-spec, 07-06) brzmi: żaden pełny rewrite ani migracja obecnego kodu na Vercel nie jest dziś uzasadniona kosztowo — oba środowiska (W3, W4) zostają na bms-1. Jedyna spójna, warunkowa rekomendacja to wąski, ograniczony rewrite W3 do Next.js/Supabase jako proof-of-concept (6 tyg., €20k, tylko RESO+Gratus — najbardziej ryzykowny fragment stacku), z W4 pozostawionym bez zmian — to jedyna opcja, którą przynajmniej jeden dokument spójnie popiera, reszta to warianty “to zależy od MRR/wzrostu, których nie znamy.”

15.4 Środowisko staging — już istnieje, ale zdegradowane; NIE potrzeba nowego serwera

Odpowiedź wprost na pytanie “osobny serwer czy bms-1”: żadne z dwóch — staging już istnieje na bms-3, i istniejąca rekomendacja mówi “przenieś na bms-4”, nie “kup nowy serwer” ani “rób to na bms-1”.

Co realnie istnieje dziś na bms-3:

  • Prawdziwe, działające kontenery: v42-stage, s3-v42-stage, v41-stage, v31-stage (uruchomione od 3-6 miesięcy), plus osierocony s3-v32-stage (peer v32-stage usunięty).
  • Realne, testowalne URL-e stagingowe dla mikroserwisów: mailgun-v42-stage.dev.pinbox24.com, s3-v2-test.dev.pinbox24.com (docs/playbooks/pinbox24-ms-deploy.md).

Ale jest zdegradowany, nie czysty CI-testowany staging:

  • v32-stage był w pętli crash-restart PM2 przez ~4 miesiące (~124% CPU, 1,2M restartów, zero aktywnych deploymentów) — usunięty. Jego towarzysz s3-v32-stage pozostał osierocony.
  • Sprzeczność w dokumentacji: pinbox24-ms-deploy.md deklaruje “Staging host: bms-3”, ale osobny playbook (pinbox24-docker-compose-staging-prod-collision.md) opisuje incydent, gdzie te same mikroserwisy (mailgun, s3-v2) staging i produkcja kolidowały ze sobą na bms-1 (współdzielony katalog builda) — w praktyce staging bywał deployowany na tym samym hoście co produkcja, nie na bms-3, wbrew etykiecie w dokumentacji.
  • Brak zautomatyzowanego etapu staging-deploy w CI — deploye stagingowe (docker-deploy-stage.sh) są ręczne/ad-hoc, ten sam wzorzec kruchości co produkcja (§15.1).
  • Żadna izolowana sieć Docker dla stagingu nie jest udokumentowana na żadnym hoście — test-net (nazwa sugerująca staging) to w rzeczywistości sieć produkcyjna bms-1 dla v4.x.

Realna dostępność zasobów na bms-3 — niewystarczająca dla pełnego stagingu: MongoDB PRIMARY/SECONDARY zużywa ~21,7GB z 32GB RAM na bms-3, zostawiając ~10,3GB współdzielone między OS+Docker+11 kontenerów. Udokumentowane jako ryzyko P1 (P1-05): skok RAM (test obciążeniowy stagingu, restart kontenera, build Dockera) może zabić MongoDB PRIMARY przez OOM i wywołać nieplanowaną elekcję rs0.

Istniejąca, gotowa rekomendacja (nie trzeba wymyślać nowej): docs/evaluation/02-bms-modernization-plan.md (Task 2.7) rekomenduje: ustawić limit cache WiredTiger na bms-3 jako natychmiastową ulgę, a następnie przenieść kontenery stagingu na bms-4 (1,8TB dysku, 32GB RAM, tylko ~75MB zużyte przez arbiter MongoDB — mnóstwo realnego zapasu). To jest jedyna spójna rekomendacja we wszystkich przeanalizowanych dokumentach i jest to przeniesienie na istniejący serwer, nie zakup nowego.

Ocena i rekomendacja (synteza dla użytkownika): Nie potrzeba dziś nowego serwera do stagingu. Trzy kroki, w tej kolejności: (1) posprzątać istniejący staging na bms-3 (usunąć osierocone s3-v32-stage, ograniczyć WiredTiger cache), (2) przenieść kontenery stagingu na bms-4 zgodnie z gotową rekomendacją (usuwa ryzyko OOM/rs0-election), (3) dopiero wtedy budować faktyczny zautomatyzowany krok CI staging-deploy — dziś to ręczne skrypty ad-hoc, dokładnie ten sam wzorzec kruchości co produkcja (§15.1) i musi być naprawiony razem, inaczej “w pełni działający staging” pozostanie aspiracją, nie faktem.

15.5 Synteza — jak te 4 ustalenia się łączą

Te cztery wątki nie są niezależne — kruchość redeploymentu (§15.1) jest tym samym problemem, który uniemożliwia zaufanie zarówno produkcji, jak i stagingowi (§15.4): dopóki hotfixy nie są trwałe (git + bind-mount, nie pamięć kontenera), żaden staging — nawet idealnie odizolowany — nie odda wiernie tego, co faktycznie działa na produkcji. Kolejność działań ma znaczenie:

  1. Dokończyć konwersję patchy na trwałe (w toku, §15.1) — warunek wstępny dla sensownego stagingu, bo inaczej staging testuje inny kod niż produkcja.
  2. Uporządkować/przenieść staging na bms-4 (§15.4) — niezależne od decyzji Angular vs Next.js, potrzebne w każdym scenariuszu.
  3. Upgrade OS bms-1 (§15.2) — blokada niezależna od wyboru frameworka, zablokowana na człowieku (okno serwisowe), ale nie blokuje kroków 1-2.
  4. Decyzja Angular vs Next.js (§15.3) — nadal otwarta, zależna od danych biznesowych (MRR, % przychodu RESO, trajektoria wzrostu), których żaden analizowany dokument nie dostarcza. Jedyna spójna rekomendacja: wąski rewrite W3 (RESO+Gratus) jako PoC, W4 bez zmian — ale to wymaga decyzji użytkownika, nie technicznej.

Kluczowe ustalenia (§15)

  1. Redeploy Pinbox24 jest dziś kruchy — dopiero od 2-3 dni w drodze do powtarzalności, nie “stabilnie opracowany.”
  2. EOL Ubuntu 20.04 na bms-1 to blokada bezpieczeństwa niezależna od decyzji Angular/Next.js — dotyczy obu scenariuszy.
  3. Decyzja “przepisać czy zostać” NIE jest podjęta mimo nazwy dokumentu sugerującej inaczej — trzy dokumenty z tygodnia dają trzy różne, warunkowe kierunki.
  4. Pinbox24 jest już częściowo multi-tenant na poziomie danych (współdzielone w3_db/ w4_db przez officeId) — to zmienia ramę “rewrite żeby uzyskać multi-tenancy.”
  5. Liczba “7 klientów” nie zgadza się dokładnie z rzeczywistą listą (9 aktywnych, 8 po wykluczeniu Eco-Trans) — do zweryfikowania przed decyzją biznesową.
  6. Środowisko staging już istnieje (bms-3) — nie potrzeba nowego serwera, potrzeba uporządkowania i przeniesienia na bms-4 zgodnie z już istniejącą rekomendacją.

Rekomendacje (§15)

  • P0 — Dokończyć konwersję pozostałych plików-łatek na trwały wzorzec (bind-mount+git): excel_import.js (w3), docker-deploy-prod.sh credentiale (#3063), hasło Mongo w logach PM2 (#3060) — warunek wstępny dla sensownego stagingu. (excel_import.js skreślone — nie jest plikiem-łatką; śledzony przez git w p24-v-3.2, źródło prawdy = GitLab. Zweryfikowane #4708 2026-07-30.)
  • P0 — Naprawić otwarte bugi kruchości redeployu: #2826 (sieć v32-prod znika po recreate), #3096 (GUS WSDL znika po recreate).
  • P1 — Posprzątać staging na bms-3 (usunąć s3-v32-stage, ograniczyć WiredTiger cache) i przenieść na bms-4 zgodnie z docs/evaluation/02-bms-modernization-plan.md Task 2.7 — NIE kupować nowego serwera.
  • P1 — Zweryfikować z użytkownikiem realne dane biznesowe (MRR, % przychodu RESO, trajektoria wzrostu klientów) — bez nich decyzja Angular vs Next.js pozostanie nierozstrzygalna niezależnie od tego, ile razy się do niej wróci.
  • P1 — Zweryfikować rzeczywistą listę klientów (9 aktywnych wg system-analysis.md vs “7” używane w dokumentach ROI) przed jakąkolwiek decyzją kosztową.
  • P2 — Zaplanować okno serwisowe na upgrade bms-1 do Ubuntu 22.04 (docs/playbooks/bms1-ubuntu-2204-upgrade.md, wymaga człowieka) — niezależnie od decyzji Angular/Next.js.
  • P2 — Rozszerzyć propozycję skanowania CVE obrazów (docs/improvements/08-image-cve-scanning.md, dziś tylko stack monitoringu) o obrazy Pinbox24 — jeśli decyzja to “zostać przy Angularze.”
  • P2 — Zbudować faktyczny automatyczny krok CI staging-deploy (dziś ręczne skrypty ad-hoc) — dopiero to da “w pełni działające środowisko staging”, o które prosi użytkownik. Runbook upgrade’u to czysta praca OS/kernel, niezależna od tego, co działa w kontenerach — CVE kernela kumulują się niezależnie od tego, czy stack zostaje Angularem czy jest przepisywany. Ryzyko na poziomie aplikacji (stary Angular 7, brak skanowania CVE obrazów, nieotagowane/lokalne obrazy, nieznane root/non-root) jest realne i osobno poważne, ale to druga, dziś nieskanowana oś ryzyka, wymagająca inwestygacji w repo GitLab aplikacji, nie w p24-infra. Jeśli decyzja to “zostać przy stacku” — obie osie wymagają adresowania: upgrade OS (zablokowany na człowieku, runbook gotowy) ORAZ osobno, zbudowanie skanowania CVE obrazów + audytu Dockerfile/base-image dla repo Pinbox24 (dziś poza zakresem propozycji #08).