
---

## Wątek KB — krok 7 (pilot retrieval) + GPU: FAZA 2 MODUŁU 5 DOMKNIĘTA

**GPU przywrócone**: sekcja deploy w compose odkomentowana (merge `ollama-gpu-restore`), recreate, bge-m3 na 100% GPU. Benchmark: **207ms/embed vs 790ms CPU (~3.8× sekwencyjnie)** — overhead HTTP dominuje przy pojedynczych requestach, batching pozostaje dźwignią (backlog, przed fazą mailową). Zagadka znikającego kontenera po reboocie 15.07: jednorazowa, po boocie 16.07 wszystko wstało samo.

**Krok 7 — pilot retrieval (2683 chunki, zapytania przez bge-m3 + `<=>` cosine):**
- Kalibracja progów: **<0.45 trafienie, 0.45–0.55 szara strefa, >0.55 brak odpowiedzi** (negatywna kontrola "sernik" = 0.62, czysta separacja)
- Cross-lingual potwierdzony: angielskie zapytanie o FLL scoring lepsze niż polskie, wyciąga scoresheet mimo mojibake
- Najlepszy wynik: "innovation project scoring" → 0.387, top-5 spójnie z właściwego dokumentu
- **Chunking 600/150 zatwierdzony bez zmian**
- Findings do fazy 3 (nieblokujące): filtr chunków z binarnym OCR-szumem (przed kompilacją wiki!), deduplikacja dokumentów (paperless:14 ≡ 74), jakość OCR scoresheets

**Faza 2 modułu 5: kroki 1–8 komplet.** Baza: 225 216 kopert (gmail+paperless, cross-source), 2683 chunki z embeddingami, retrieval zweryfikowany. Następne: faza 3 — recon-plan (streszczenia+tagi jako pierwszy krok kompilacji, docelowo wiki-kompilat wg Karpathy'ego; Claude ma przygotować szkic sekcji wiki do recon-planu).

---

## Wątek control-plane — druga połowa dnia: "czemu supervisor nie generuje akcji naprawczych"

**Punkt wyjścia**: pytanie operatora o pustą kolejkę akcji odsłoniło wielowarstwową awarię warstwy decyzyjnej control-plane. Cztery kolejne fixy, każdy odkrywał następną warstwę:

### 1. RECON lustro shipping (Fable, commit `542bba4`)

`docs/infra/lustro-shipping-recon-2026-07-16.md` — 1507 mismatchy `lustro event=dead prom=up` w trwałym logu WYJAŚNIONE: (a) wczorajszy kontrolowany test (node-agent stał 3h20m, nie 15 min jak zakładano) + (b) poranny boot-race 56s. **Werdykt: shipping lustro działa, ZERO recurring problemu.** Prometheus 0 pomyłek w 48h — wzmacnia rekomendację GO dla Etapu 3 cutoveru.

Znaleziska poboczne: lustro biega na obrazie sprzed 5 tyg (deploy-node bez `--build` — patrz fix #2 niżej); fake-hwclock boot-race (RPi bez RTC — pierwszy event po boocie ma stary stempel, dropnięty przez timestamp checkpoint).

### 2. FIX `deploy-node.sh --build` (commit `77defff`)

`deploy-node.sh` robił `docker compose up -d` BEZ `--build` → dla serwisów z Dockerfile zmiany kodu NIE wchodziły (cicha rozbieżność repo↔runtime — backlog item z 2026-07-15, dziś naprawiony). Ugryzło 3×: fleet-prometheus, ha-diag, lustro.

Fix: warunkowy `--build` gdy serwis ma Dockerfile (`test -f services/<svc>/Dockerfile`), prebuilt bez `--build`. Zweryfikowany w boju na PIHA (6 serwisów: node-agent/ha-diag/brain-watchdog/llm-gateway → Building; vikunja/kb-postgres → prebuilt).

Edge case zgłoszony jako follow-up: agent-system ma build w podkatalogach bez top-level Dockerfile (niezarejestrowany w tej detekcji).

### 3. FIX supervisor resilience (commit `409b583`)

Supervisor był **funkcjonalnie zamrożony ~24h** — kontener `healthy`, ale pętla nie tikała, zero logów. Root cause zweryfikowany na `/proc` (proces `State:S hrtimer_nanosleep`, NIE deadlock): `reconcile()` robi `glob` po `EVENTS_DIR` co cykl, a to 358k plików → cykl przekracza timeout → nigdy się nie kończy. Plus logi na DEBUG = niewidoczne.

Fix: każdy cykl w `ThreadPoolExecutor` z `future.result(timeout=90s, env SUPERVISOR_RECONCILE_TIMEOUT)`; try/except owija cykl (wyjątek nie zabija pętli); tick-log co 10 cykli (env `SUPERVISOR_TICK_LOG_EVERY`) na INFO; healthcheck sprawdza świeżość heartbeat (nie samo że proces żyje). Zdeployowany. Po deployu: pętla tika (cycle #340→#480), cykl #1 timeoutował (ERROR "did not complete within 90s") ale pętla szła dalej = odporność działa.

### 4. FIX event flood + retencja + cleanup (commit `dff76ec`) — sedno problemu

Root cause braku akcji: `EVENTS_DIR` = 358k plików, 91% to `service_healthy` (szum "serwis zdrowy" emitowany co cykl per serwis).

**Krytyczne odkrycie**: mechanizm retencji `_cleanup_control_plane_fs()` był martwy od wczorajszego fixu checkpointu (`d5139c9`, 2026-07-15) — kod porównywał `str(ścieżka) <= checkpoint_int` → `TypeError` cicho łapany przez `except` → retencja przestała działać → backlog rósł bez ograniczenia. Naprawiając checkpoint wczoraj, złamaliśmy retencję, która na nim polegała.

Fix:
- (a) node-agent emituje `service_healthy` TYLKO na transition unhealthy→healthy (nie co cykl) — funkcja zachowana: `observer.process_event` nadal konsumuje `service_healthy` do `services.json[key].status=healthy` + rozwiązuje incydent, więc nie wycięte, tylko ograniczone do transition;
- (b) retencja naprawiona epoch-do-epoch (ta sama logika co `_checkpoint_ts_from_value`);
- (c) skrypt `scripts/maintenance/cleanup_event_backlog.py` (dry-run + `--apply`, `min_age` 3600s, kasuje tylko `service_healthy`/`node_health` starsze niż checkpoint, zachowuje `healthcheck_failed`/incydenty/wszystkie sygnały).

150 testów pass. Zdeployowany node-agent na PIHA+VPS. Cleanup wykonany: usunięto 272 232 pliki, backlog 358k→12,7k. Ghost dir `be17cb6eb0f6` usunięty. **Wynik**: reconcile supervisora przestał timeoutować (0 "did not complete" w 5 min), glob 12,7k, mózg odetkany.

### Stan końcowy

Control-plane supervisor odblokowany (tika, reconcile się kończy, retencja działa automatycznie co cykl). Ale kolejka akcji nadal pusta — bo (a) `shadow_mode=True` downgrade'uje HA `container_restart` do `alert_only`, (b) wpisy `error` w topologii to ghost hash-prefixed w world_state observera (NIE żywe kontenery — `docker ps -a exited=0` na VPS), observer nie czyści wpisów po zniknięciu kontenerów.

### Lekcje

- **Cichy efekt uboczny migracji typów.** Fix checkpointu `d5139c9` (ścieżka→epoch int) cicho zepsuł retencję `_cleanup_control_plane_fs()`, bo ta porównywała `str <= int` i łapała `TypeError` w szerokim `except`. Migracja typu pola musi audytować WSZYSTKICH konsumentów tego pola, nie tylko miejsce zmiany — szeroki `except` maskuje dokładnie tę klasę regresji.
- **"Healthy kontener ≠ tikająca pętla."** Docker healthcheck sprawdzający tylko "czy proces żyje" nie łapie pętli zawieszonej na blokującym wywołaniu bez timeoutu. Healthcheck musi sprawdzać świeżość heartbeat/ostatniego cyklu, nie tylko czy proces odpowiada.
- **`service_healthy` jako plik-event per cykl to antywzorzec.** Stan ("serwis jest zdrowy") emitowany jako zdarzenie na każdym cyklu miesza dwa różne pojęcia (stan vs zdarzenie) i skaluje się liniowo z (liczba serwisów × liczba cykli) — dokładnie to, co wygenerowało 358k plików. Emitować tylko na transition.
- **Równoległe sesje = ciągłe rozjazdy mastera.** Kilka CC działających jednocześnie na różnych worktree podnosi ryzyko push divergence — dyscyplina "zatrzymaj się i zgłoś" przy konflikcie jest tańsza niż force-push.

### Otwarte (następne sesje, osobne świadome tematy)

- Ghost-wpisy w world_state observera (hash-prefixed `error` w topologii mimo 0 exited kontenerów — observer nie prune'uje zniknionych kontenerów ze stanu). To trzyma `System Status: ERROR` fałszywie.
- `shadow_mode` → remediacja: decyzja czy włączyć auto-restart padłych kontenerów (architektura, guardraile, cooldowny).
- Czemu supervisor nie generuje akcji redeploy mimo widocznych realnych error (elasticsearch/diskover na piha, ollama solaria) — drift→action nie domyka się.
- gokapi `.env` not found (deploy-node VPS rzuca błąd na gokapi — brakujący `.env`).
- Etap 3 cutover per-node (dane gotowe, recon GO).

---

## Wątek KB — faza 3, krok 2 (migracja 004 + pilot streszczeń): W TOKU

**Migracja 004 (document_summary)** zastosowana na kb-postgres@PIHA: summary, tags JSONB, model, embedding+HNSW, UNIQUE z model (pozwala trzymać oba tory pilota jednocześnie).

**Pilot dwutorowy (decyzja D3 planu):**
- Tor lokalny (gemma3:12b, GPU): 155/157 streszczeń + embeddingi. Po drodze **realny bug**: Ollama domyślnie ucina kontekst do ~2048 tok niezależnie od advertised 128k modelu — 71/157 dok. (45%) miało obcięte streszczenia (np. paperless:12 opisywał RODO zamiast treści AutoCasco, bo widział tylko pierwsze ~8k znaków). Naprawione: jawny `num_ctx` per wywołanie skalowany do długości treści + regression-guard w testach. Cały tor przeliczony od zera po fixie: 27 min na 157 dokumentów, zero CPU-offloadu (48/49 warstw GPU przez cały run).
- Tor referencyjny (Claude Haiku 4.5, API): 157/157 streszczeń + embeddingi, koszt ~1.4 USD zgodny z szacunkiem. Klucz API przez plik poza repo, nigdy nie trafił do transkryptu, usunięty po użyciu.

**Stan:** commit `33f9944` na `task/kb-f3-summaries` (niezmergowany — worktree żyje, dokończenie w następnej sesji). Migracja + oba tory + fix num_ctx + testy gotowe.

**Do zrobienia następnym razem (ten sam worktree):** rebase na master (sąsiad wjechał w międzyczasie) → porównanie A/B ~15 dokumentów (Haiku vs gemma3, ocena przez Oskara) → weryfikacja końcowa (bilans, sanity SQL, retrieval po embeddingu streszczenia) → testy+commit+push → merge → decyzja o modelu dla fazy mailowej.

**Lekcja do zapamiętania:** każde wywołanie generatywne przez Ollamę musi jawnie ustawiać `num_ctx` — domyślna wartość runtime (~2048) cicho ucina długie dokumenty niezależnie od deklarowanego rozmiaru kontekstu modelu. Dotyczy też przyszłej syntezy w fazie 5.
