homelab-codex-ws/docs/sessions/2026-07-16.md
oskar 510fe0b600 feat(kb): frontmatter OKF dla 39 session logow
Session logi zostaja w docs/sessions/ (decyzja z etapu 1). Dodany wylacznie
blok frontmattera: type: session-log, visibility: private, status: active,
updated = data ostatniego commita pliku.

Tresc nietknieta — kazdy plik to +9/-0 linii.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:58:04 +02:00

99 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
okf: "0.1"
type: session-log
visibility: private
status: active
updated: 2026-07-16
links: []
---
---
## 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.450.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 18 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.