diff --git a/docs/sessions/2026-08-06.md b/docs/sessions/2026-08-06.md index a3f0176..e2003ec 100644 --- a/docs/sessions/2026-08-06.md +++ b/docs/sessions/2026-08-06.md @@ -289,3 +289,208 @@ nigdzie nie prowadzą, i jest głównym powodem, dla którego `events/lustro/` m ### Narrative > _user-provided summary_ + +--- + +## Session 15:25 + +Wdrożenie do runtime dwóch fixów zmergowanych na `master` (supervised, checkpointy +zatwierdzane przez operatora): dispatch `0o775` + rc=23 w executorze/node-agencie +(`52eca1c`) oraz zdjęcie mitygacji M1 na SOLARII i VPS (`1bab321`). Domyka follow-upy +#1 i #5 z sesji 13:20. + +### Commits + +Ta sesja **nie wprowadziła żadnych commitów** poza niniejszym logiem — deploy runtime, +zero zmian w kodzie. Wdrożone commity powstały wcześniej, na branchu +`task/dispatch-perms-m1`: + +``` +1bab321 revert(m1): zdjecie NODE_TYPE=lte_node na SOLARII i VPS po wdrozeniu R1 +52eca1c fix(dispatch): inbox 0o775 + rsync rc=23 przestaje byc cichy +``` + +W trakcie sesji main checkout przesunął się o `75d9695 docs(recon): przyrostowka IMAP +gmail + fastmail` (druga sesja operatora, docs-only). Bez wpływu: `node_agent.py` ma to +samo `sha256 aec6cb03` w obu drzewach, więc SOLARIA — zdeployowana jeszcze z `1bab321` — +nie rozjechała się z VPS-em deployowanym z `75d9695`. + +### Files changed + +Brak — drzewo robocze czyste przez całą sesję, poza tym plikiem. + +### Deploys + +| Node | Serwis | Przed | Po | Wynik | +|---|---|---|---|---| +| SOLARIA | node-agent | `node_agent.py` `fee079e8`, obraz `438111e2` | `aec6cb03` == repo HEAD, obraz `bc28a30a` | OK | +| VPS | node-agent | `node_agent.py` `c21967d3`, obraz `27be875d` | `aec6cb03` == repo HEAD | OK | +| VPS | control-plane | `executor.py` `5ca0490e`, 4 obrazy z 2026-08-05 | `1c3b569f` == repo HEAD, 4 obrazy przebudowane | OK | + +Mechanizm: `scripts/deploy/deploy-service.sh --build-if-needed` dla node-agenta (ta sama +ścieżka co 2026-08-05). **Nie** użyto `scripts/deploy/deploy.sh `: jest to +dyspozytor Saturn-side po SSH, deployujący *cały* node — na VPS ruszyłby npm, outline, +joplin i ai-cluster, czyli daleko poza zakres, a sesja toczyła się z SOLARII (`ssh solaria` +to połączenie do samego siebie). + +`NODE_TYPE` po zdjęciu M1: SOLARIA `lte_node` → **`ai_node`** (jawnie w override), +VPS `lte_node` → **linia usunięta**, `NODE_TYPE=""` z base compose → `_resolve_node_type()` +zwraca `standard`. Log startowy potwierdza jedno i drugie (`type=ai_node`, `type=standard`), +czyli przewidywanie z commita `1bab321` co do pustego stringa było trafne. + +Gate testowy: **pytest niedostępny w main checkoucie** (`.venv` bez pytest, +`~/.local/bin/pytest` ma zepsuty `_pytest`). Oparto się na wyniku sprzed merge'a +(node-agent 70 passed, control-plane 173 passed) plus `docker build` obu stacków przy +deployu. Follow-up 15:25/#4. + +### Pierwszy cykl cleanup po zdjęciu M1 + +Na obu nodach marker `/opt/homelab/state/last-docker-cleanup` **nie istniał** (M1 blokował +zapis od 2026-08-04), więc `_cleanup_rate_ok()` zwrócił `True` i prune poszedł w pierwszym +cyklu, ~0,5 s po starcie — zgodnie z ostrzeżeniem w `1bab321`. Dlatego kolejność w każdym +kroku była: **najpierw tagi rollback, potem deploy.** + +SOLARIA: +``` +INFO - node-agent starting: node=solaria type=ai_node +INFO - Pruned dangling images (0 MB reclaimed) +INFO - No disposable stopped containers (kept 0 managed) +INFO - Pruned build cache (91 MB reclaimed) +``` +VPS: +``` +INFO - node-agent starting: node=vps type=standard +INFO - Pruned dangling images (0 MB reclaimed) +INFO - No disposable stopped containers (kept 0 managed) +INFO - Pruned build cache (239 MB reclaimed) +``` + +**Zero ubytków kontenerów na obu nodach** — `docker ps -a` przed vs po, diff nazw pusty +(SOLARIA 9/9, VPS 24/24). `humanai-mailer` i `humanai-landing` (bez definicji w repo) +nietknięte. Tagi `rollback-*` przeżyły prune. + +Dwie prognozy przedwdrożeniowe wymagały korekty — obie z tego samego powodu, że +`docker images` pokazuje **rozmiar pozorny z warstwami współdzielonymi**, a nie realny +odzysk: + +* **SOLARIA, „4 dangling ≈ 553 MB":** cztery obrazy faktycznie zniknęły (35 → 32, przy + +1 nowym buildzie), ale `SpaceReclaimed` = **0 MB**. Ich warstwy są współdzielone z + control-plane i kb-query. Realny odzysk obrazów ≈ 70 MB (17,89 → 17,82 GB); z build + cache (606,1 → 510,5 MB) łącznie ≈ 165 MB. +* **VPS, „1 dangling 395 MB":** ten obraz to **żywy `outline-postgres-1`** — untagged, ale + oznaczony `U` (in use). Docker odmawia usunięcia obrazu używanego przez kontener, więc + prune go nie ruszył i **nie miał prawa ruszyć**. Realny odzysk to wyłącznie build cache + 239 MB (z 250,8 MB reclaimable). + +Kontener `control-plane-ui` na SOLARII stoi w stanie `created` — poza zasięgiem prune'a +podwójnie: `_prune_stopped_containers()` listuje wyłącznie `status=exited`, a kontener ma +i tak label compose oraz `restart=unless-stopped`. + +### Weryfikacja fixu dispatch end-to-end + +Stan wejściowy zdjęty **przed** deployem control-plane (patrz follow-up 15:25/#1 — inaczej +`deploy-local.sh` by go zatarł): + +| ścieżka | mode | | +|---|---|---| +| `actions/dispatch/piha` | 775 | historycznie działał | +| `actions/dispatch/lustro` | **755** | wyciek | +| `actions/deploy` | 755 | inbox deploy-runnera, bez podkatalogów | + +**Korekta do sekcji „Sprzątanie" z sesji 13:20.** Zapis „`actions/dispatch/lustro/` na VPS +— opróżniony przez operatora (zombie re-pull ustał)" **nie odzwierciedla stanu +faktycznego**: o 13:16 oba pliki (z 10:39 i 11:08) nadal leżały w źródle, a LUSTRO +re-pullowało je co 60 s aż do 13:18:23. Katalog zdrenował się dopiero w wyniku poniższego +testu — i mógł, bo dopiero wtedy miał prawa `775`. + +Mechanizm potwierdzony co do joty: inbox `aerbot:aerbot 755`, a ciągnie z niego +`VPS_EVENTS_USER=oskar` — uid **1002**, tylko *członek* grupy `aerbot` (gid 1000). Grupa ma +`r-x` bez `w`, więc `--remove-source-files` nie może zrobić unlinku: + +``` +13:15:13 INFO - Action container-restart-lustro-node-exporter already processed — skipping +13:15:13 INFO - Action container-restart-lustro-watchtower already processed — skipping +13:16:16 ... (to samo) 13:17:19 ... (to samo) +``` + +**Test HITL.** Cel zmieniony z `watchtower` na `node-exporter`: watchtower na LUSTRO jest +w crash-loopie (137 restartów, `restart=always` — root cause opisany w KROKU 4b sesji +13:20), więc nie dałby czystego sygnału. Nowe `action_id` +(`container-restart-lustro-node-exporter-dispatchfix`), bo recykling starego odbiłby się od +bramki idempotencji na LUSTRO i zawiesiłby akcję w `running` do timeoutu. + +**Ścieżka: przez `approved/`, nie przez approval operatora.** Plik `pending` zapisany +13:16:56 UTC został auto-anulowany przez supervisora po **7 sekundach** +(`drift_resolved_auto` — `node-exporter` był zdrowy), zanim operator zdążył kliknąć. To +dokładnie zjawisko z sekcji „Obserwacja uboczna: regeneracja i auto-cancel" sesji 13:20, +tym razem szybsze (7 s vs 15 s). Zgodnie z ustaleniem przed testem druga kopia trafiła +prosto do `approved/`; cel testu to mechanika dispatchu, nie ścieżka approvalu. Pole +`status` w tej kopii zostało `cancelled` — bez znaczenia, executor kieruje się katalogiem, +nie polem. + +Wynik — wszystkie sześć kryteriów spełnione: + +1. **755 → 775** dokładnie w momencie zapisu przez executora (13:17:23) ✅ +2. plik akcji **zniknął** ze źródła po pobraniu i **nie wrócił** w kolejnych cyklach ✅ +3. **oba zaległe pliki zdrenowane przy okazji** — `dispatch/lustro/` całkowicie pusty ✅ +4. spam `already processed — skipping` **ustał**: dwa pełne cykle (13:19:25, 13:20:28) z + zerem trafień ✅ +5. akcja `completed` przez `action_result` po 70 s, `node-exporter` z nowym uptime ✅ +6. zero ERROR/WARNING w executorze, observerze, supervisorze i node-agentach — poza znanym, + niezwiązanym crash-loopem watchtowera na LUSTRO ✅ + +`13:18:23 INFO - Restarted container 'node-exporter' for action +container-restart-lustro-node-exporter-dispatchfix` + +**Czego ten test NIE dowiódł.** Druga połowa fixu — klasyfikacja rc=23 z WARNING-iem — na +LUSTRO nie pojechała: node-agent tam ma nadal `fee079e8` (ten sam sha, który SOLARIA miała +przed dzisiejszym deployem). `grep -c "rc=23"` = 0 w logach LUSTRO dlatego, że stary kod +fizycznie nie umie tego zalogować, a nie dlatego, że jest dobrze. Follow-up 15:25/#3. + +### Stan tagów rollback + +| Node | Tag | Image ID | +|---|---|---| +| SOLARIA | `node-agent:rollback-pre-dispatchfix` | `438111e2` | +| SOLARIA | `node-agent:rollback-pre-r1` (z 2026-08-05) | `c80a711f` | +| VPS | `node-agent:rollback-pre-dispatchfix` | `27be875d` | +| VPS | `control-plane-executor:rollback-pre-dispatchfix` | `b878d835` | +| VPS | `control-plane-observer:rollback-pre-dispatchfix` | `4b79511d` | +| VPS | `control-plane-supervisor:rollback-pre-dispatchfix` | `357b70d1` | +| VPS | `control-plane-operator-ui:rollback-pre-dispatchfix` | `e522bbaa` | + +Pierwsze tagi `rollback-*` na VPS w ogóle. Otagowano wszystkie cztery obrazy control-plane, +nie tylko executora — `deploy-local.sh` przebudowuje cały stack, więc każdy potrzebuje celu +rollbacku. + +### Follow-upy (sesja 15:25) + +1. **`deploy-local.sh` robi rekurencyjny `chown` + `chmod` na całym `/opt/homelab`** — do + przeglądu, czy to w ogóle pożądane. Oba branche by dziś zadziałały: `sudo chown -R + 1000:1000` (wyzwalacz: `events/solaria/evt-solaria-1785946768-node_health-node.json`) i + `sudo chmod -R 775` (wyzwalacze: `actions/dispatch/lustro`, `actions/deploy`). Ten drugi + ustawiłby `dispatch/lustro` na 775 z zupełnie innego powodu niż fix w executorze, + zacierając stan wejściowy testu, i nadałby 775 *plikom* w całym drzewie runtime (stąd + `-rwxrwxr-x` na heartbeatach i JSON-ach akcji). **Świadoma decyzja operatora: nie + naprawiać ręcznie.** Control-plane zdeployowano samym krokiem compose (`up -d --build + --force-recreate`) — bez sudo, więc rozjazdy chown/chmod zostały nietknięte i czekają na + przegląd. Dodatkowo `sudo` na VPS wymaga hasła, więc kanoniczna ścieżka i tak kończyłaby + się handoffem (exit 5). +2. **Lokalny control-plane na SOLARII** (executor/observer/supervisor/ui, `dispatch/solaria/` + z 2026-07-22) ma nadal executor z bugiem `0o755`. Nie jest dyspozytorem floty — log to + same `Starting executor loop` — ale to drugi, niezdeployowany egzemplarz tego samego + kodu. Osobna sesja. +3. **LUSTRO i PIHA bez dzisiejszego node-agenta.** LUSTRO: `fee079e8`. Bez fixu rc=23 + nadal *połykają* nieudany unlink — dziś to nie boli, bo executor już nie tworzy złych + inboxów, ale każdy przyszły wyciek o innej przyczynie znowu będzie niewidoczny. +4. **Brak pytest w main checkoucie na SOLARII** — `deploy.sh ` przewróciłby się na + gate (exit 2) bez `--no-gate`. Do naprawy, zanim ktoś sięgnie po kanoniczną ścieżkę + deployu z tej maszyny. +5. **Duplikat akcji testowej** — `container-restart-lustro-node-exporter-dispatchfix.json` + leży jednocześnie w `completed/` (właściwy przebieg) i w `cancelled/` (kopia z + auto-anulowania sprzed approvalu). Pozostawione bez zmian — kosmetyka, żaden zapis + produkcyjny nie był potrzebny. + +### Narrative + +> _user-provided summary_ diff --git a/docs/sessions/2026-08-07.md b/docs/sessions/2026-08-07.md deleted file mode 100644 index a2bd42a..0000000 --- a/docs/sessions/2026-08-07.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -okf: "0.1" -type: session-log -visibility: private -status: active -updated: 2026-08-06 -links: [] ---- - -# Session log 2026-08-07 - -> **Uwaga do datowania.** Praca wykonana **2026-08-06, 14:10–15:25 CEST**. Plik nosi datę -> następnego dnia na wyraźne życzenie operatora, bo `docs/sessions/2026-08-06.md` jest już -> zajęty przez sesję fazy mailowej z tego samego okna. Wszystkie znaczniki czasu w treści -> są rzeczywiste (2026-08-06). - -## Session 15:25 - -Wdrożenie do runtime dwóch fixów zmergowanych na `master` (supervised, checkpointy -zatwierdzane przez operatora): dispatch `0o775` + rc=23 w executorze/node-agencie -(`52eca1c`) oraz zdjęcie mitygacji M1 na SOLARII i VPS (`1bab321`). - -### Commits - -Ta sesja **nie wprowadziła żadnych commitów** poza niniejszym logiem — deploy runtime, -zero zmian w kodzie. Wdrożone commity powstały wcześniej, na branchu -`task/dispatch-perms-m1`: - -``` -1bab321 revert(m1): zdjecie NODE_TYPE=lte_node na SOLARII i VPS po wdrozeniu R1 -52eca1c fix(dispatch): inbox 0o775 + rsync rc=23 przestaje byc cichy -``` - -W trakcie sesji main checkout przesunął się o `75d9695 docs(recon): przyrostowka IMAP -gmail + fastmail` (druga sesja operatora, docs-only). Bez wpływu: `node_agent.py` ma to -samo `sha256 aec6cb03` w obu drzewach, więc SOLARIA — zdeployowana jeszcze z `1bab321` — -nie rozjechała się z VPS-em deployowanym z `75d9695`. - -### Files changed - -Brak — drzewo robocze czyste przez całą sesję, poza tym plikiem. - -### Deploys - -| Node | Serwis | Przed | Po | Wynik | -|---|---|---|---|---| -| SOLARIA | node-agent | `node_agent.py` `fee079e8`, obraz `438111e2` | `aec6cb03` == repo HEAD, obraz `bc28a30a` | OK | -| VPS | node-agent | `node_agent.py` `c21967d3`, obraz `27be875d` | `aec6cb03` == repo HEAD | OK | -| VPS | control-plane | `executor.py` `5ca0490e`, 4 obrazy z 2026-08-05 | `1c3b569f` == repo HEAD, 4 obrazy przebudowane | OK | - -Mechanizm: `scripts/deploy/deploy-service.sh --build-if-needed` dla node-agenta (ta sama -ścieżka co 2026-08-05). **Nie** użyto `scripts/deploy/deploy.sh `: jest to -dyspozytor Saturn-side po SSH, deployujący *cały* node — na VPS ruszyłby npm, outline, -joplin i ai-cluster, czyli daleko poza zakres, a sesja toczyła się z SOLARII (`ssh solaria` -to połączenie do samego siebie). - -`NODE_TYPE` po zdjęciu M1: SOLARIA `lte_node` → **`ai_node`** (jawnie w override), -VPS `lte_node` → **linia usunięta**, `NODE_TYPE=""` z base compose → `_resolve_node_type()` -zwraca `standard`. Log startowy potwierdza jedno i drugie (`type=ai_node`, `type=standard`), -czyli przewidywanie z commita `1bab321` co do pustego stringa było trafne. - -Gate testowy: **pytest niedostępny w main checkoucie** (`.venv` bez pytest, -`~/.local/bin/pytest` ma zepsuty `_pytest`). Oparto się na wyniku sprzed merge'a -(node-agent 70 passed, control-plane 173 passed) plus `docker build` obu stacków przy -deployu. Follow-up #4. - -### Pierwszy cykl cleanup po zdjęciu M1 - -Na obu nodach marker `/opt/homelab/state/last-docker-cleanup` **nie istniał** (M1 blokował -zapis od 2026-08-04), więc `_cleanup_rate_ok()` zwrócił `True` i prune poszedł w pierwszym -cyklu, ~0,5 s po starcie — zgodnie z ostrzeżeniem w `1bab321`. Dlatego kolejność w każdym -kroku była: **najpierw tagi rollback, potem deploy.** - -SOLARIA: -``` -INFO - node-agent starting: node=solaria type=ai_node -INFO - Pruned dangling images (0 MB reclaimed) -INFO - No disposable stopped containers (kept 0 managed) -INFO - Pruned build cache (91 MB reclaimed) -``` -VPS: -``` -INFO - node-agent starting: node=vps type=standard -INFO - Pruned dangling images (0 MB reclaimed) -INFO - No disposable stopped containers (kept 0 managed) -INFO - Pruned build cache (239 MB reclaimed) -``` - -**Zero ubytków kontenerów na obu nodach** — `docker ps -a` przed vs po, diff nazw pusty -(SOLARIA 9/9, VPS 24/24). `humanai-mailer` i `humanai-landing` (bez definicji w repo) -nietknięte. Tagi `rollback-*` przeżyły prune. - -Dwie prognozy przedwdrożeniowe wymagały korekty — obie z tego samego powodu, że -`docker images` pokazuje **rozmiar pozorny z warstwami współdzielonymi**, a nie realny -odzysk: - -* **SOLARIA, „4 dangling ≈ 553 MB":** cztery obrazy faktycznie zniknęły (35 → 32, przy - +1 nowym buildzie), ale `SpaceReclaimed` = **0 MB**. Ich warstwy są współdzielone z - control-plane i kb-query. Realny odzysk obrazów ≈ 70 MB (17,89 → 17,82 GB); z build - cache (606,1 → 510,5 MB) łącznie ≈ 165 MB. -* **VPS, „1 dangling 395 MB":** ten obraz to **żywy `outline-postgres-1`** — untagged, ale - oznaczony `U` (in use). Docker odmawia usunięcia obrazu używanego przez kontener, więc - prune go nie ruszył i **nie miał prawa ruszyć**. Realny odzysk to wyłącznie build cache - 239 MB (z 250,8 MB reclaimable). - -Kontener `control-plane-ui` na SOLARII stoi w stanie `created` — poza zasięgiem prune'a -podwójnie: `_prune_stopped_containers()` listuje wyłącznie `status=exited`, a kontener ma -i tak label compose oraz `restart=unless-stopped`. - -### Weryfikacja fixu dispatch end-to-end - -Stan wejściowy zdjęty **przed** deployem control-plane (patrz follow-up #1 — inaczej -`deploy-local.sh` by go zatarł): - -| ścieżka | mode | | -|---|---|---| -| `actions/dispatch/piha` | 775 | historycznie działał | -| `actions/dispatch/lustro` | **755** | wyciek | -| `actions/deploy` | 755 | inbox deploy-runnera, bez podkatalogów | - -Mechanizm potwierdzony co do joty: inbox `aerbot:aerbot 755`, a ciągnie z niego -`VPS_EVENTS_USER=oskar` — uid **1002**, tylko *członek* grupy `aerbot` (gid 1000). Grupa ma -`r-x` bez `w`, więc `--remove-source-files` nie może zrobić unlinku. Dwa pliki akcji z -10:39 i 11:08 leżały w źródle, a LUSTRO re-pullowało je **co 60 s**, odbijając od bramki -idempotencji: - -``` -13:15:13 INFO - Action container-restart-lustro-node-exporter already processed — skipping -13:15:13 INFO - Action container-restart-lustro-watchtower already processed — skipping -13:16:16 ... (to samo) 13:17:19 ... (to samo) -``` - -**Test HITL.** Cel zmieniony z `watchtower` na `node-exporter`: watchtower na LUSTRO jest -w crash-loopie (137 restartów, `restart=always`, nie potrafi pullnąć lokalnego obrazu -`piper-tts-rpi5` — 401 z Docker Huba), więc nie dałby czystego sygnału. Nowe `action_id` -(`container-restart-lustro-node-exporter-dispatchfix`), bo recykling starego odbiłby się od -bramki idempotencji na LUSTRO i zawiesiłby akcję w `running` do timeoutu. - -**Ścieżka: przez `approved/`, nie przez approval operatora.** Plik `pending` zapisany -13:16:56 UTC został auto-anulowany przez supervisora po **7 sekundach** -(`drift_resolved_auto` — `node-exporter` był zdrowy), zanim operator zdążył kliknąć. Zgodnie -z ustaleniem przed testem druga kopia trafiła prosto do `approved/`; cel testu to mechanika -dispatchu, nie ścieżka approvalu. Pole `status` w tej kopii zostało `cancelled` — bez -znaczenia, executor kieruje się katalogiem, nie polem. - -Wynik — wszystkie sześć kryteriów spełnione: - -1. **755 → 775** dokładnie w momencie zapisu przez executora (13:17:23) ✅ -2. plik akcji **zniknął** ze źródła po pobraniu i **nie wrócił** w kolejnych cyklach ✅ -3. **oba zaległe pliki zdrenowane przy okazji** — `dispatch/lustro/` całkowicie pusty ✅ -4. spam `already processed — skipping` **ustał**: dwa pełne cykle (13:19:25, 13:20:28) z - zerem trafień ✅ -5. akcja `completed` przez `action_result` po 70 s, `node-exporter` z nowym uptime ✅ -6. zero ERROR/WARNING w executorze, observerze, supervisorze i node-agentach — poza znanym, - niezwiązanym crash-loopem watchtowera na LUSTRO ✅ - -`13:18:23 INFO - Restarted container 'node-exporter' for action -container-restart-lustro-node-exporter-dispatchfix` - -**Czego ten test NIE dowiódł.** Druga połowa fixu — klasyfikacja rc=23 z WARNING-iem — na -LUSTRO nie pojechała: node-agent tam ma nadal `fee079e8` (ten sam sha, który SOLARIA miała -przed dzisiejszym deployem). `grep -c "rc=23"` = 0 w logach LUSTRO dlatego, że stary kod -fizycznie nie umie tego zalogować, a nie dlatego, że jest dobrze. Follow-up #3. - -### Stan tagów rollback - -| Node | Tag | Image ID | -|---|---|---| -| SOLARIA | `node-agent:rollback-pre-dispatchfix` | `438111e2` | -| SOLARIA | `node-agent:rollback-pre-r1` (z 2026-08-05) | `c80a711f` | -| VPS | `node-agent:rollback-pre-dispatchfix` | `27be875d` | -| VPS | `control-plane-executor:rollback-pre-dispatchfix` | `b878d835` | -| VPS | `control-plane-observer:rollback-pre-dispatchfix` | `4b79511d` | -| VPS | `control-plane-supervisor:rollback-pre-dispatchfix` | `357b70d1` | -| VPS | `control-plane-operator-ui:rollback-pre-dispatchfix` | `e522bbaa` | - -Pierwsze tagi `rollback-*` na VPS w ogóle. Otagowano wszystkie cztery obrazy control-plane, -nie tylko executora — `deploy-local.sh` przebudowuje cały stack, więc każdy potrzebuje celu -rollbacku. - -### Follow-upy - -1. **`deploy-local.sh` robi rekurencyjny `chown` + `chmod` na całym `/opt/homelab`** — do - przeglądu, czy to w ogóle pożądane. Oba branche by dziś zadziałały: `sudo chown -R - 1000:1000` (wyzwalacz: `events/solaria/evt-solaria-1785946768-node_health-node.json`) i - `sudo chmod -R 775` (wyzwalacze: `actions/dispatch/lustro`, `actions/deploy`). Ten drugi - ustawiłby `dispatch/lustro` na 775 z zupełnie innego powodu niż fix w executorze, - zacierając stan wejściowy testu, i nadałby 775 *plikom* w całym drzewie runtime (stąd - `-rwxrwxr-x` na heartbeatach i JSON-ach akcji). **Świadoma decyzja operatora: nie - naprawiać ręcznie.** Control-plane zdeployowano samym krokiem compose (`up -d --build - --force-recreate`) — bez sudo, więc rozjazdy chown/chmod zostały nietknięte i czekają na - przegląd. Dodatkowo `sudo` na VPS wymaga hasła, więc kanoniczna ścieżka i tak kończyłaby - się handoffem (exit 5). -2. **Lokalny control-plane na SOLARII** (executor/observer/supervisor/ui, `dispatch/solaria/` - z 2026-07-22) ma nadal executor z bugiem `0o755`. Nie jest dyspozytorem floty — log to - same `Starting executor loop` — ale to drugi, niezdeployowany egzemplarz tego samego - kodu. Osobna sesja. -3. **LUSTRO i PIHA bez dzisiejszego node-agenta.** LUSTRO: `fee079e8`. Bez R-fixu rc=23 - nadal *połykają* nieudany unlink — dziś to nie boli, bo executor już nie tworzy złych - inboxów, ale każdy przyszły wyciec o innej przyczynie znowu będzie niewidoczny. -4. **Brak pytest w main checkoucie na SOLARII** — `deploy.sh ` przewróciłby się na - gate (exit 2) bez `--no-gate`. Do naprawy, zanim ktoś sięgnie po kanoniczną ścieżkę - deployu z tej maszyny. -5. **`watchtower` na LUSTRO w crash-loopie** — 137 restartów, `restart=always`, próbuje - pullnąć lokalnie zbudowany `piper-tts-rpi5:latest` z Docker Huba i dostaje 401. - Niezwiązane z tym deployem, ale generuje WARNING co 60 s w logach node-agenta. -6. **Duplikat akcji testowej** — `container-restart-lustro-node-exporter-dispatchfix.json` - leży jednocześnie w `completed/` (właściwy przebieg) i w `cancelled/` (kopia z - auto-anulowania sprzed approvalu). Pozostawione bez zmian — kosmetyka, żaden zapis - produkcyjny nie był potrzebny. - -### Narrative - -> _user-provided summary_