docs(sesja): 2026-07-16 control-plane — dopisanie 4 fixow (recon lustro, deploy-node --build, supervisor freeze, event flood) + aktualizacja backlogu
Sesja odkryla i naprawila wielowarstwowa awarie warstwy decyzyjnej: pusta kolejka akcji byla skutkiem zamrozonej petli supervisora (glob 358k eventow > timeout) i martwej retencji (cichy efekt uboczny fixu checkpointu z 07-15). Backlog: oznaczone ZROBIONE (deploy-node --build, supervisor frozen, event flood/retencja), dodane OTWARTE (ghost world_state, shadow_mode decyzja, drift->action, gokapi .env). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
dff76ecd30
commit
893a0de862
|
|
@ -74,6 +74,41 @@ Zweryfikować po zmianie, że `npm_api.py --npm vps` nadal łączy się przez
|
||||||
wpis „ZNIKNĘŁY (potwierdzone reconem 2026-07-02)" w Zamkniętych zdezaktualizowany.
|
wpis „ZNIKNĘŁY (potwierdzone reconem 2026-07-02)" w Zamkniętych zdezaktualizowany.
|
||||||
**Fix**: ręczny `docker rm` hash-prefixed kontenerów control-plane na VPS;
|
**Fix**: ręczny `docker rm` hash-prefixed kontenerów control-plane na VPS;
|
||||||
przy okazji sprawdzić, skąd wróciły (fix A `3b71707` miał blokować źródło divergence).
|
przy okazji sprawdzić, skąd wróciły (fix A `3b71707` miał blokować źródło divergence).
|
||||||
|
**Update 2026-07-16** (sesja `docs/sessions/2026-07-16.md`): po odetkaniu supervisora
|
||||||
|
(event flood + pętla zamrożona, oba naprawione dziś) potwierdzone, że wpisy `error`
|
||||||
|
w topologii panelu to dokładnie te ghosty — hash-prefixed w world_state observera,
|
||||||
|
NIE żywe kontenery (`docker ps -a exited=0` na VPS). Observer nie prune'uje wpisów
|
||||||
|
po zniknięciu kontenerów spod tych nazw. To trzyma `System Status: ERROR` fałszywie.
|
||||||
|
**Fix (do zrobienia)**: observer powinien weryfikować faktyczny stan kontenera przy
|
||||||
|
budowaniu world_state i prune'ować wpisy dla kontenerów, których już nie ma (ta sama
|
||||||
|
klasa błędu co „Rozjazd world-state observera: NOMINAL przed istnieniem" niżej).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### shadow_mode → remediacja: decyzja o auto-restart
|
||||||
|
|
||||||
|
**Data**: 2026-07-16
|
||||||
|
**Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane
|
||||||
|
**Problem**: po odetkaniu supervisora (event flood + pętla zamrożona, oba naprawione
|
||||||
|
dziś) kolejka akcji nadal pusta częściowo dlatego, że `shadow_mode=True` downgrade'uje
|
||||||
|
HA `container_restart` do `alert_only` — supervisor widzi problem, ale świadomie nie
|
||||||
|
enqueue'uje akcji restartu.
|
||||||
|
**Do decyzji**: czy i kiedy włączyć auto-restart padłych kontenerów — wymaga
|
||||||
|
architektury (guardraile, cooldowny, blast radius per serwis) zanim `shadow_mode=false`.
|
||||||
|
Patrz też istniejący wpis „ha-diag-agent deploy ZABLOKOWANY" niżej — przed
|
||||||
|
`shadow_mode=false` tam wymieniony konkretny target (`homeassistant5`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### gokapi: deploy-node VPS rzuca błąd — brakujący `.env`
|
||||||
|
|
||||||
|
**Data**: 2026-07-16
|
||||||
|
**Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane
|
||||||
|
**Problem**: `deploy-node.sh` na VPS rzuca błąd na serwisie gokapi z powodu brakującego
|
||||||
|
`.env` (`/opt/homelab/config/gokapi/.env` nieutworzony lub niepełny — wzorzec z sesji
|
||||||
|
2026-07-09 `docs/sessions/2026-07-09-kb-configi-gokapi.md`).
|
||||||
|
**Fix**: sprawdzić `services/gokapi/env.example`, utworzyć/uzupełnić `.env` na VPS wg
|
||||||
|
konwencji `env.example` → `/opt/homelab/config/<service>/.env`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -203,6 +238,13 @@ czy ma własną logikę de-duplifikacji blokującą enqueue.
|
||||||
**Update 2026-07-02**: ghost kontenery (bug B) zniknęły z VPS — jeśli objaw wróci,
|
**Update 2026-07-02**: ghost kontenery (bug B) zniknęły z VPS — jeśli objaw wróci,
|
||||||
hipoteza "źródłem są ghosty" jest już nieaktualna. UWAGA: ślepy supervisor na SATURN
|
hipoteza "źródłem są ghosty" jest już nieaktualna. UWAGA: ślepy supervisor na SATURN
|
||||||
(brak mountu repo, patrz sesja 2026-07-02) to INNY przypadek — nie mylić z tym bugiem.
|
(brak mountu repo, patrz sesja 2026-07-02) to INNY przypadek — nie mylić z tym bugiem.
|
||||||
|
**Update 2026-07-16** (sesja `docs/sessions/2026-07-16.md`, druga połowa dnia): dwie
|
||||||
|
głębsze przyczyny pustej kolejki znalezione i naprawione (petla supervisora zamrożona
|
||||||
|
~24h — patrz „Supervisor: pętla zamrożona…" w Zamkniętych; event flood 358k plików
|
||||||
|
paraliżujący reconcile — patrz „Event flood…" w Zamkniętych). Po obu fixach supervisor
|
||||||
|
tika i reconcile się kończy, ALE objaw z tego wpisu (brak `redeploy` mimo widocznego
|
||||||
|
`error` — elasticsearch/diskover na piha, ollama solaria) **nadal aktualny** — drift→action
|
||||||
|
nie domyka się mimo odetkanego mózgu. Zostaje otwarte jako osobne dochodzenie.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -325,6 +367,44 @@ Długoterminowo: `agent.sh new` powinien odmawiać jeśli żądana gałąź jest
|
||||||
|
|
||||||
## Zamknięte
|
## Zamknięte
|
||||||
|
|
||||||
|
### Supervisor: pętla zamrożona ~24h, healthy ale nie tika — NAPRAWIONE (2026-07-16, commit `409b583`)
|
||||||
|
|
||||||
|
**Data**: 2026-07-16
|
||||||
|
**Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane
|
||||||
|
**Było**: kontener supervisora `healthy`, ale pętla `reconcile()` nie tikała od ~24h,
|
||||||
|
zero logów. Root cause zweryfikowany na `/proc` (`State:S hrtimer_nanosleep`, NIE
|
||||||
|
deadlock): `glob` po `EVENTS_DIR` co cykl przy 358k plikach → cykl przekracza brak
|
||||||
|
timeoutu → nigdy się nie kończy. Logi na DEBUG maskowały objaw.
|
||||||
|
**Naprawione**: 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 (`SUPERVISOR_TICK_LOG_EVERY`) na INFO; healthcheck sprawdza
|
||||||
|
świeżość heartbeat, nie tylko czy proces żyje. Zweryfikowane w boju: pętla tika
|
||||||
|
(cycle #340→#480), cykl #1 timeoutował ale pętla kontynuowała = odporność działa.
|
||||||
|
**Lekcja**: „healthy kontener ≠ tikająca pętla" — healthcheck musi sprawdzać
|
||||||
|
świeżość ostatniego cyklu, nie samo czy proces odpowiada.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Event flood 358k plików + retencja martwa od fixu checkpointu — NAPRAWIONE (2026-07-16, commit `dff76ec`)
|
||||||
|
|
||||||
|
**Data**: 2026-07-16
|
||||||
|
**Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane
|
||||||
|
**Było**: `EVENTS_DIR` = 358k plików, 91% to `service_healthy` emitowany co cykl per
|
||||||
|
serwis (stan-jako-zdarzenie, antywzorzec). Retencja `_cleanup_control_plane_fs()`
|
||||||
|
była martwa od fixu checkpointu `d5139c9` (2026-07-15, patrz „Bug: checkpoint
|
||||||
|
observera po ścieżce leksykalnej" niżej) — porównanie `str(ścieżka) <= checkpoint_int`
|
||||||
|
rzucało `TypeError` cicho łapany przez szeroki `except` → backlog rósł bez ograniczenia.
|
||||||
|
Naprawiając checkpoint wczoraj, złamaliśmy retencję, która na nim polegała.
|
||||||
|
**Naprawione**: (a) node-agent emituje `service_healthy` tylko na transition
|
||||||
|
unhealthy→healthy (funkcja dla `observer.process_event` zachowana); (b) retencja
|
||||||
|
naprawiona epoch-do-epoch; (c) `scripts/maintenance/cleanup_event_backlog.py`
|
||||||
|
(dry-run + `--apply`). 150 testów pass. Cleanup wykonany na PIHA+VPS: 272 232 pliki
|
||||||
|
usunięte, backlog 358k→12,7k. **Wynik**: reconcile supervisora przestał timeoutować.
|
||||||
|
**Lekcja**: migracja typu pola (ścieżka→epoch int) musi audytować WSZYSTKICH
|
||||||
|
konsumentów tego pola — szeroki `except` maskował dokładnie tę klasę regresji.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Ghost kontenery w panelu (Problem B) — ZNIKNĘŁY (potwierdzone reconem 2026-07-02)
|
### Ghost kontenery w panelu (Problem B) — ZNIKNĘŁY (potwierdzone reconem 2026-07-02)
|
||||||
|
|
||||||
> ⚠️ ZDEZAKTUALIZOWANE 2026-07-06: ghosty znów widoczne na VPS — patrz wpis
|
> ⚠️ ZDEZAKTUALIZOWANE 2026-07-06: ghosty znów widoczne na VPS — patrz wpis
|
||||||
|
|
@ -718,7 +798,7 @@ liveness). Dotyczy tez przyszlych: nextcloud, gokapi.
|
||||||
**Zasada na przyszlosc:** rejestracja w services.yaml/topology to CZESC deployu, nie osobny
|
**Zasada na przyszlosc:** rejestracja w services.yaml/topology to CZESC deployu, nie osobny
|
||||||
krok "kiedys" — inaczej kazdy nowy serwis to slepy punkt monitoringu.
|
krok "kiedys" — inaczej kazdy nowy serwis to slepy punkt monitoringu.
|
||||||
|
|
||||||
## Bug: deploy-node.sh nie przebudowuje obrazu — deploy "OK" ale nowy kod nie wchodzi (2026-07-15)
|
## Bug: deploy-node.sh nie przebudowuje obrazu — deploy "OK" ale nowy kod nie wchodzi (2026-07-15) — ✅ ZROBIONE (2026-07-16, commit `77defff`)
|
||||||
|
|
||||||
**Objaw.** `deploy-node.sh <serwis>` robi `docker compose up -d` BEZ `--build`. Dla
|
**Objaw.** `deploy-node.sh <serwis>` robi `docker compose up -d` BEZ `--build`. Dla
|
||||||
serwisów z Dockerfile (ha-diag-agent, node-agent, llm-gateway, brain-watchdog, itd.),
|
serwisów z Dockerfile (ha-diag-agent, node-agent, llm-gateway, brain-watchdog, itd.),
|
||||||
|
|
@ -736,10 +816,12 @@ force-recreate) i ha-diag-agent 2026-07-15 (fix node_name był w repo `f2ba81b`,
|
||||||
compose. Docker cache'uje obraz po tagu, nie po zawartości src/.
|
compose. Docker cache'uje obraz po tagu, nie po zawartości src/.
|
||||||
compose. Docker cache'uje obraz po tagu, nie po zawartości src/.
|
compose. Docker cache'uje obraz po tagu, nie po zawartości src/.
|
||||||
|
|
||||||
**Fix (do zrobienia).** deploy-node.sh powinien dla serwisów z Dockerfile wywoływać
|
**Fix — ZROBIONE (2026-07-16, `77defff`).** deploy-node.sh wywołuje teraz `--build`
|
||||||
`docker compose up -d --build` (przebuduje gdy src się zmienił; no-op gdy nie). Ewentualnie
|
warunkowo, gdy serwis ma top-level `Dockerfile` (`test -f services/<svc>/Dockerfile`);
|
||||||
`--force-recreate` gdy zmienił się env-file. Bez tego każdy code-only deploy wymaga
|
prebuilt serwisy bez `--build` (no-op). Zweryfikowane w boju na PIHA: 6 serwisów
|
||||||
ręcznego rebuild — łatwo przeoczyć (deploy mówi green).
|
(node-agent/ha-diag/brain-watchdog/llm-gateway → Building; vikunja/kb-postgres → prebuilt).
|
||||||
|
**Follow-up pozostawiony**: `agent-system` ma build w podkatalogach bez top-level
|
||||||
|
Dockerfile — niezarejestrowany przez tę detekcję, osobny task.
|
||||||
|
|
||||||
## Ollama SOLARIA: brak sterownika NVIDII — ZAMKNIĘTE (2026-07-16)
|
## Ollama SOLARIA: brak sterownika NVIDII — ZAMKNIĘTE (2026-07-16)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,61 @@
|
||||||
- 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
|
- 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).
|
**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).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue