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 (patrzdocs/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
| # | Sekcja | Status | Notatka |
|---|---|---|---|
| 0 | Metodologia i zakres | ✅ | ten plik |
| 1 | Streszczenie zarządcze | ✅ done | patrz §1 |
| 2 | Inwentaryzacja architektury | ✅ done | patrz §2 — odsyła do istniejącej taksonomii 18-perspektyw |
| 3 | Audyt i unifikacja dokumentacji | ✅ done | patrz §3 — P0 już wykonane: linki dodane do docs/index.md |
| 4 | System ról Claude (CLAUDE.md, prompty, local/remote) | ✅ done | patrz §4 — znaleziono 4 niezależne systemy ról |
| 5 | Głęboka analiza dispatchera/kolejki | ✅ done | patrz §5 — w tym §5.6 auto-close issues |
| 6 | Alerting SLA — czemu 20 zgłoszeń/dobę wisi | ✅ done | patrz §6 — znaleziono definitywną przyczynę |
| 7 | Redesign zgłaszania incydentów (infra + security) | ✅ done | patrz §7 |
| 8 | Capacity planning — nowy serwer | ✅ done | patrz §8 — strategia już mówi: bms-3 najpierw, HW dopiero gdy się wysyci |
| 9 | Gotowość produkcyjna — 3. licencja Claude / MCP w produkcji EcoTrans | ✅ done | patrz §9 — w tym architektura lekkich workerów jednorazowych |
| 10 | Dashboard kliencki (Portal) — monitoring maili/WhatsApp | ✅ done | patrz §10 — portal dziś to jeden płaski panel, zero multi-tenant |
| 11 | Synteza słabości organizacyjnych/procesowych | ✅ done | patrz §11 — 4 wzorce przekrojowe |
| 12 | Rekomendacje i roadmapa priorytetowa | ✅ done | patrz §12 — 32 pozycje P0/P1/P2 |
| 13 | Załączniki / surowe dane z agentów | ✅ done | patrz §13 |
| 14 | Higiena równoległości sesji + stacja dewelopera jako węzeł mocy | ✅ done | patrz §14 (dopisane w toku sesji) |
| 15 | Pinbox24 — stabilność redeploymentu, Angular vs Next.js, środowisko staging | ✅ done | patrz §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ń
- 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)
- 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)
- System alertów potrafi cicho anulować zadanie bez ŻADNEGO powiadomienia —
cancel_unroutable_nc_alert_batch()po 15 min, zero śladu. (§6.3) - 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)
- 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)
- 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) - 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)
- 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)
- 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)
- 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-architecture … 18-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
- 18-perspektywowa taksonomia (
docs/01-*…docs/18-*) — czysta, każdy folder maREADME.md, linkowana zdocs/index.md. To jest właściwy “hub nawigacyjny”. - 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 wdocs/roles/). - 112 luźnych plików
.mdbezpośrednio wdocs/— 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.mdz linkami doanaliza-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/*.mddodocs/roles/<rola>/. Rekomendacje P0/P1 - Dispatcher (§5.3):
docs/worker-queue-operations.mdsprzeczny zdocs/playbooks/queue-dispatcher-priority.mdco 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)
- 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.
- 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.mdsekcję 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 dodocs/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
| # | System | Lokalizacja | Kto to ładuje | Role |
|---|---|---|---|---|
| A | Główna persona interaktywna | C:\Users\konar\.claude\agent-prompts\roles\*.md (11 plików, tylko Windows, nie w git) | Skille ~/.claude/commands/role-<name>.md | sys-admin, sys-security, secret-manager, dev-coder, dev-reviewer, dev-tester, marketing-director, content-creator, video-producer, designer, + p24-network-admin (sierota, brak skilla) |
| B | Repo-lokalny indeks playbooków | docs/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 A | Te same 10 co A, minus p24-network-admin |
| C | Cross-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.md | sysadmin, debugger, security-auditor, secret-manager — inne nazwy, debugger nie istnieje nigdzie indziej |
| D | Zdalny headless worker | infra/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_PROFILE | Zgrubny 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 | ✅ obszerny | ✅ infra-task-request-worker.md |
| sys-security | ✅ | ✅ | ✅ | ❌ brak — żaden job_type/ROLE nie mapuje się na promptu security-workera |
| secret-manager | ✅ | ✅ | ✅ obszerny | ✅ worker-secret-manager.md v2.0.0 — ale to inna treść niż wersja A |
| dev-coder | ✅ | ✅ | ✅ | ✅ worker-issue.md, worker-nextjs-dev.md, worker-python-dev.md |
| dev-reviewer | ✅ | ✅ | ✅ | ✅ review-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
p24-network-adminjest realny, ale wpół-podłączony. Wymieniony w macierzy rólp24-infra/CLAUDE.md§Role Enforcement (linia 252), ma pełny plik persony i playbook operacyjny — ale brak skilla/role-p24-network-admin, brak indeksu wdocs/roles/, nieobecny w obu tabelach “10 ról” (docs/roles/README.mdidocs/playbooks/roles-and-skills.md). Można go dziś aktywować tylko ręcznie wklejając ścieżkę pliku w promptAgent(). Klasyczny dryf: dodany do jednego rejestru, nigdy nie rozpropagowany do pozostałych trzech.- 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. Jegosecret-manager.mdto inny plik niż wersje z systemów A i D — trzy odrębne definicje “secret managera” bez wspólnego źródła.debuggerisecurity-auditor(z formalnym protokołem izolacji sesji/adwersarialnego audytu) nie mają odpowiednika nigdzie w A/B. - 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 zawszeC:\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 przezsecrets-sync.ymlani żadną rolę Ansible. W praktyce zdalne workeryclaude -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. docs/roles/<rola>/index.mdto czysta dokumentacja — nieużywana przez żaden skill ani worker programowo.
Kluczowe ustalenia (§4)
- 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.
- 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.
p24-network-adminto rola “widmo” — zdefiniowana, ale niedostępna przez normalny interfejs/role-*.- 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/*.mddo repo (docs/roles/<rola>/persona.md), zastępując dzisiejszy nagłówkowy stub windex.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-adminbrakujący skill/role-p24-network-admin.mdi 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ąć koncepcjedebugger/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)
| Awaria | Przyczyna źródłowa | Powtó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ęty | Wzorzec (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 crash | Supabase 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 loop | reset_stale_workers() resetował zawieszone zadania do queued BEZ inkrementacji retry_count → nieskończona ~2h pętla | Nie powtórzyła się od migracji fix | Trwał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ż scalone | Nie w pełni trwała — maskowanie i hard-affinity do jednego hosta to nadal otwarte punkty w samym playbooku |
| Dead-column references | Migracja 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 exhaustion | reset_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 ujemny | Naprawione migracją 037 | Trwał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-batchjest 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.mdtwierdzi że vps-i1 jest “disabled” a bms-4 to “jedyny aktywny host workerów”, podczas gdyqueue-dispatcher-priority.md(aktualizowany 2026-07-01) idocs/priorities.mdopisują 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=255dla 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 (
playwrightblokowany <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_queuejest 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 zinformation_schema.columns. - Maskowanie HTTP 5xx jako
::warning::wnc-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)
- Redundancja dispatchera jest w większości udokumentowana, nie udowodniona w praktyce — realnie działa tylko bms-4.
- 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.
- 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ą.
- 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.mdz realnym stanemdev_r_server_capacity(który host jest faktycznieenabled=true) — jedno źródło prawdy, reszta linkuje. - P0 — Potwierdzić czy maskowanie HTTP 5xx w
nc-alert-instant-dispatch.ymlzostał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_capacitylubdev_r_worker_queuewymusza 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 (playbookprovision-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-triage —
raz 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:
- 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.
- Twardy sufit pojemności — 1 slot workera na jedynym hoście, który w ogóle może uruchomić krok inwestygacji.
- Luka routingu — ciche anulowanie po 15 min bez fallbacku (patrz §6.3) oznacza, że część alertów nie dostaje NAWET inwestygacji.
Kluczowe ustalenia (§6)
- 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ą.
- Realny cykl naprawy to do 24h (raz dziennie), nie 2-3 minuty.
- 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-triagedodatkowy, lekki przebieg śróddzienny (co 4-6h, ograniczony wyłącznie do zamykania/eskalacji issuesp24-infra-nc-alertjuż oznaczonychai-dev-queuedprzez 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.mdsugerują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ło | Kto | Maszyna | Kiedy | Sesja | Dlaczego (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.py → dev_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:
- 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.
Readna pliku poświadczeń który nie jest zablokowany, albo udanesops -dbez flag wypisujące na stdout), nigdy nie dotrze do tego hooka. - Brak PostToolUse na
Readw ogóle — istnieje tylkoPreToolUse(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ącegodev_r_incidents) z obowiązkowymi kolumnami:reporter(kto/co — human email alboclaude-session:<id>),machine(hostname/IP — dziś nieobecne nigdzie),session_id(klucz łączący zagent_errors— dziś nieobecne pozaagent_errors),context(jaka rola/zadanie),comment(dlaczego),reported_at. Skryptopen-incident.pyjuż 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ścitool_responseniezależnie od exit code (nie tylko przy błędzie), (b) nowyPostToolUsehook naReadskanują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 kolumnamireporter/machine/comment(dziś brakującymi nawet wagent_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)
- 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.
- 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. - 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 kolumnyreporter,machine,commentjako NOT NULL — dziś nawet najbardziej strukturalna tabela ich nie ma. - P0 — Rozszerzyć hook o skanowanie niezależne od exit code + nowy
PostToolUsenaRead(patrz §7.2) — to zamyka lukę “udana komenda wycieka sekret bez wykrycia”. - P1 — Zbudować widoki SQL/skrypt generujący
docs/secrets-rotation-log.mdidocs/pinbox24/incident-register.mdz tabeli zamiast ręcznego dopisywania — koniec rozjazdu między “co się stało” a “co jest zapisane”. - P1 — Dodać
session_idjako wspólny klucz obcy międzyagent_errors,dev_r_incidentsi 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)
| Serwer | Rdzenie | RAM | max_workers dziś | Rola |
|---|---|---|---|---|
| bms-1 | 8 vCPU | 32 GB | 0 (brak agenta) | Pinbox24 prod (Ubuntu 20.04 EOL) |
| bms-2 | 8 vCPU | 32 GB | 0 | MongoDB rs0 PRIMARY |
| bms-3 | 8 vCPU | 32 GB | 0 dziś; strategia proponuje 3 | MongoDB SECONDARY + Pinbox24 staging |
| bms-4 | 8 vCPU | 32 GB | max_workers=4, emergency_max_workers=12 | n8n + Redis + Mongo arbiter + główny dispatch |
| vps-i1 | 6 vCPU | 8 GB | musi zostać 0 (incydent #2979 — OOM po włączeniu) | Monitoring stack |
| vps-h1 | niepotwierdzone (dokumentacja przestarzała) | niepotwierdzone | n/a | Zamroż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)
- 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).
- 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.
- 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
- 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ę przezANTHROPIC_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:
- 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.
- 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_KEYprzedclaude -p). - 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.
- 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)
- 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”.
- 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.
- 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 przezANTHROPIC_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→ Supabasewhatsapp_messages(statusnew) → 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
- Brak modelu auth per-klient — portal ma jeden poziom dostępu; potrzebna nowa rola
(np.
client_readonly) + scoping wierszy poclient_id/office_id, albo custom claim Supabase Auth + polityka RLS (brak precedensu w repo — to będzie pierwszy taki wzorzec). - 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 WhatsApp —
whatsapp_messages/p24_l_emails_inboxmają dane per-wiersz, ale brak widoku/materialized view (dzienne liczniki, heartbeat “czy proces żyje”). - 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ą.
- 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 MongoDBregRecords.processStatusId→ liczniki sukces/porażka/duplikat per dzień per office_id, (b) rollupemwhatsapp_messagesstatus per grupa/dzień. - Kolumna
client_id/office_idna każdym wierszu + polityka RLSclient_id = auth.jwt() ->> 'client_id'— pierwszy w tym repo wzorzec per-tenant, warto udokumentować jako nowy standard wdocs/supabase-operations.mdskoro 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)
- 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.
- Portal dziś nie ma ŻADNEGO konceptu multi-tenant — to od zera, nie rozszerzenie.
- 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.mdvsqueue-dispatcher-priority.mdsprzeczne (§5.3) — nowsza prawda w playbooku, starsza (błędna) w ops-doc, nikt nie zaktualizował drugiego. - Hub nawigacyjny:
docs/index.mdnie 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-batchhard-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)
- 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ą.
- Redundancja infrastrukturalna jest w dużej mierze iluzoryczna — jeden realny host (bms-4) dźwiga większość przepływu.
- System dobrze wykrywa głośne awarie, słabo wykrywa ciche/częściowe.
- 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łanie | Sekcja | Status |
|---|---|---|---|
| 1 | Skorygować udokumentowany model SLA alertów: instant-dispatch = komentarz inwestygacyjny (2-15 min), naprawa = raz dziennie (20:00 UTC) — nie 2-3 min | §6.2 | ⏳ |
| 2 | cancel_unroutable_nc_alert_batch() — przestać cicho anulować, dodać komentarz GH + Discord | §6.3 | ⏳ |
| 3 | Potwierdzić, czy maskowanie HTTP 5xx w nc-alert-instant-dispatch.yml jest naprawione (playbook mówił “PR pending”) | §5.5 | ⏳ |
| 4 | Zunifikować worker-queue-operations.md z realnym stanem dev_r_server_capacity | §5.3 | ⏳ |
| 5 | Scalić 4 systemy ról → jedno źródło prawdy w docs/roles/<rola>/persona.md | §4.3 | ⏳ |
| 6 | Nadać p24-network-admin brakujący skill /role-p24-network-admin | §4.3 | ⏳ |
| 7 | Rozszerzyć agent_errors/incident log o kolumny reporter/machine/comment (NOT NULL) | §7.3 | ⏳ |
| 8 | Rozszerzyć hook bezpieczeństwa o skan niezależny od exit-code + nowy PostToolUse na Read | §7.3 | ⏳ |
| 9 | Dokończyć re-provisioning bms-3 (2-tyg. soak test) PRZED zakupem nowego serwera | §5.3, §8.3 | ⏳ |
| 10 | Dopisać linki do analiza-infrastruktury.md/strategia-p24-infra-2026.md/docs/audits/INDEX.md w docs/index.md | §3.3 | ✅ zrobione w tej sesji |
| 11 | Dodać monitoring/heartbeat dla procesów MCP produkcyjnych EcoTrans PRZED włączeniem na produkcję | §9.2 | ⏳ |
P1 — kolejne 2-4 tygodnie
| # | Działanie | Sekcja |
|---|---|---|
| 12 | CI/pre-merge gate wykrywający rozjazd kolumn schema (bash/Python/SQL) po migracjach dev_r_* | §5.5 |
| 13 | Dodać bms-4 jako fallback dla nc-alert-batch (dostęp do Prometheusa) | §6.4, §5.3 |
| 14 | Rozwiązać los systemu ról C (.claude/roles/ / /open-role) — scalić lub jawnie zdeprecjonować | §4.3 |
| 15 | Rozszerzyć credential-mapping workerów zdalnych o sys-security i dev-tester | §4.3 |
| 16 | Zbudować widoki generujące secrets-rotation-log.md/incident-register.md z jednej tabeli | §7.3 |
| 17 | Dodać session_id jako wspólny klucz między tabelami incydentów | §7.3 |
| 18 | Jeśli bms-3 się nasyci: kupić OVH Rise-3 (16 vCPU/64GB, ~€150/mies.) — NIE 24-core | §8.3 |
| 19 | Zmierzyć realne zużycie RAM/CPU n8n na bms-4 | §8.3 |
| 20 | Dokończyć infra-src/ai-worker/ + migrację 048 jako dedykowany tor dla zapytań jednorazowych (brand-strategy), rozliczany przez API key, nie seat OAuth | §9.3 |
| 21 | Zmierzyć empirycznie koszt RAM + limit RPM/TPM API po zbudowaniu — dopiero to da liczbę równoległych wątków | §9.3 |
| 22 | Zbudować client_dashboard_metrics (sync MongoDB + rollup WhatsApp) jako fundament dashboardu klienta | §10.5 |
| 23 | Zaprojektować + udokumentować pierwszy wzorzec RLS per-klient jako nowy standard | §10.5 |
P2 — w tle, gdy jest czas
| # | Działanie | Sekcja |
|---|---|---|
| 24 | Dedykowany alert “oczekiwany węzeł dispatch nieobecny >X h” | §5.5 |
| 25 | Naprawić nieaktualny tekst “co godzinę” w nightly-devops-triage.md | §6.4 |
| 26 | Ujednolicić PR-y sesyjne (rotation logi) do używania Closes #N konsekwentnie | §5.6 |
| 27 | Rozważyć krótki dashboard czytający ujednoliconą tabelę incydentów | §7.3 |
| 28 | Zweryfikować rozjazd specyfikacji vps-h1 (frozen vs wolna pojemność) | §8.3 |
| 29 | Rozszerzyć dev_r_server_capacity o wymiar seat/konto, nie tylko RAM/CPU | §9.3 |
| 30 | Decyzja: rozszerzyć portal/ czy zbudować osobną app client-facing | §10.5 |
| 31 | Dać dodatkowy śróddzienny przebieg nightly-devops-triage tylko dla nc-alert | §6.4 |
| 32 | Indeks 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:
- Dispatcher/kolejka workerów — architektura, katalog awarii, wąskie gardła (§5)
- System ról Claude — mapa 4 systemów, pokrycie per rola (§4)
- SLA alertowania — pełny łańcuch przyczynowy (§6)
- Bieżący stan zgłaszania incydentów — mapa 5+ mechanizmów (§7.1)
- Dane do capacity planning — specyfikacje serwerów, koszt RAM/workera (§8)
- Pipeline monitoringu maili/WhatsApp — źródła danych pod dashboard klienta (§10)
- Weryfikacja auto-close issues po merge PR (§5.6)
- 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, automatykidocs/strategia-p24-infra-2026.md(2026-06-27) — strategia rozwoju, capacity plandocs/priorities.md— historia 71 sesji, aktywne ryzyka P0docs/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
gitpoza 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:
SessionStart → agent-session-start.sh, Stop → agent-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:
- WSL2 z Ubuntu (rekomendowane) — zainstalować dokładnie ten sam stack co dev-laptop
(
claude-runneruser, 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 wqueue-dispatcher-loop.py. - 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_capacityma już kolumnylast_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 wierszserver_label='windows-dev'zos_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 nawetorchestrator, jeśli RAM na to pozwala) imax_weight_prime='light'(alboenabled=falsew 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)
- Dwa z trzech pytań użytkownika mają już gotowe, działające rozwiązania w repo — wymagają weryfikacji/dokończenia, nie budowy od zera.
- 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). - 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
$PROFILEna tej maszynie eksportujeP24_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-devdodev_r_server_capacityz 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/):
| Awaria | Klasa przyczyny | Ile razy się powtórzyła |
|---|---|---|
| Zahardkodowane klucze Wasabi w skompilowanym JS — rotacja usuwa klucz, obraz nadal się do niego odwołuje | konfiguracja 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-recreate | hotfix 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 recreate | konfiguracja sieci niepersystowana w compose | Otwarte, nienaprawione — issue #2826 |
Zmienne env rozdzielone między dwoma kontenerami (backend-environment.env vs s3-environment.env) — jeden zaktualizowany, drugi nie | luka propagacji credentiali/env między powiązanymi usługami | Główna przyczyna crash-loop v42-prod (506 restartów, 07-01) |
docker commit na kontenerze debugowym startowanym przez sleep wypieka zły ENTRYPOINT do obrazu | ręczny skrót zamiast prawdziwego docker build | 1× potwierdzone, przywołane jako lekcja |
| Pliki-łatki (hand-edited, bez kontroli wersji, czasem z zahardkodowanymi credentialami) istniały tylko jako stan serwera | patche nigdy w git, brak kroku CI je wdrażającego | To 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.mdjawnie 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.jsskreślone — to nie jest plik-łatka. Weryfikacja read-only na bms-1 wykazała, że jest to stockowy, śledzony przez git plik wpinbox24/p24-v-3.2(tracked + clean, ostatni commit upstream 2020-02-01), bajt-identyczny z kopią wbudowaną w obrazv32-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łyDOCKER-USER. docs/cloudflare-security-operations.mdidocs/ssh-hardening-operations.mdnie 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-placedo-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, statusproposed, P2) dotyczy wyłącznie stacku monitoringu — nie wspomina obrazów Pinbox24/bms-1. docs/update-management.mdma 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.mdstwierdza, ż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-prodna 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:
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.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.”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 osieroconys3-v32-stage(peerv32-stageusunię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-stagebył w pętli crash-restart PM2 przez ~4 miesiące (~124% CPU, 1,2M restartów, zero aktywnych deploymentów) — usunięty. Jego towarzyszs3-v32-stagepozostał osierocony.- Sprzeczność w dokumentacji:
pinbox24-ms-deploy.mddeklaruje “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:
- 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.
- Uporządkować/przenieść staging na bms-4 (§15.4) — niezależne od decyzji Angular vs Next.js, potrzebne w każdym scenariuszu.
- 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.
- 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)
- Redeploy Pinbox24 jest dziś kruchy — dopiero od 2-3 dni w drodze do powtarzalności, nie “stabilnie opracowany.”
- EOL Ubuntu 20.04 na bms-1 to blokada bezpieczeństwa niezależna od decyzji Angular/Next.js — dotyczy obu scenariuszy.
- Decyzja “przepisać czy zostać” NIE jest podjęta mimo nazwy dokumentu sugerującej inaczej — trzy dokumenty z tygodnia dają trzy różne, warunkowe kierunki.
- Pinbox24 jest już częściowo multi-tenant na poziomie danych (współdzielone
w3_db/w4_dbprzezofficeId) — to zmienia ramę “rewrite żeby uzyskać multi-tenancy.” - Liczba “7 klientów” nie zgadza się dokładnie z rzeczywistą listą (9 aktywnych, 8 po wykluczeniu Eco-Trans) — do zweryfikowania przed decyzją biznesową.
- Ś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.shcredentiale (#3063), hasło Mongo w logach PM2 (#3060) — warunek wstępny dla sensownego stagingu. (excel_import.jsskreślone — nie jest plikiem-łatką; śledzony przez git wp24-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 zdocs/evaluation/02-bms-modernization-plan.mdTask 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.mdvs “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).