Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
20 KiB
Plan rozbudowy silnika workflow
Stan na 2026-09-20. Ten dokument jest operacyjnym planem przyjętym przez właściciela. Decyzje architektoniczne pozostają w docs/DESIGN.md; tutaj zapisujemy etapy, kryteria odbioru i stan realizacji.
Tryb pracy
Od 2026-09-21 realizacja przebiega ciągle, pod odpowiedzialnością jednej sesji prowadzącej. Rezygnujemy z osobnej pętli Sol → Terra → Sol dla każdego etapu, ponieważ wielokrotne ponowne czytanie tego samego zakresu nadmiernie wydłużało pracę i zużywało kontekst.
- Sesja prowadząca realizuje kolejne etapy bez obowiązkowego przekazywania ich między modelami.
- Testy uruchamiamy po spójnych partiach funkcjonalnych, a pełny
npm run checkprzed każdym commitem. - Jeden przekrojowy audyt architektury, bezpieczeństwa, migracji i wdrożenia odbywa się przed końcowym przełączeniem na VM103.
- Dodatkową sesję wykorzystujemy tylko dla konkretnego problemu, którego równoległe rozwiązanie daje wyraźną korzyść; nie jest to obowiązkowa bramka etapu.
Każda sesja czyta AGENTS.md i TASK_STATE.md na start. docs/DESIGN.md i ten dokument czyta się przy pracy nad architekturą albo nowym etapem, nie w całości przy każdej drobnej zmianie — patrz TASK_STATE.md po bieżący stan. Nie dotyka VM103, Proxmoxa ani działających kontenerów przed końcowym, kontrolowanym wdrożeniem. Istniejące lokalne zmiany właściciela muszą pozostać nienaruszone.
Założenia przekrojowe
- Nowy subsystem powstaje obok obecnego drivera aż do kontrolowanego przełączenia.
- XState 5 jest czystym silnikiem przejść; I/O pozostaje w adapterach. Persistujemy własne projekcje i eventy domenowe.
- Opublikowane workflow, funkcje i profile są niemutowalne. Instancja ma pełny snapshot zależności.
- W kolejce może być wiele instancji
queuediwaiting; globalnie istnieje najwyżej jednarunning. - Użytkownik może składać kroki w generyczne workflow. Funkcje użytkownika i uprzywilejowane akcje systemowe są odrębnymi mechanizmami bezpieczeństwa.
- Adapter dostawcy modelu nie przenika do domeny. Profil wybiera wykonawcę jawnie i nie ma automatycznego fallbacku.
- Event ledger jest źródłem osi czasu aplikacji oraz odtwarzalnego bieżącego stanu.
Decyzja wdrożeniowa 2026-09-21
- Dzisiejszy przebieg nocny jest anulowany. Na VM103
NIGHTLY_ATzostał wyzerowany, odtworzono wyłącznie usługędriveri potwierdzono brak przebiegunightly-20260921*. - Prywatna kopia
.envsprzed zmiany znajduje się obok manifestu jako.env.before-disable-nightly-20260921; nie trafia do repozytorium. - Po zakończeniu wszystkich etapów nowy system ma zostać wdrożony na VM103, ale harmonogram nocny pozostaje wyłączony. Właściciel będzie najpierw wykonywał testy ręczne.
NIGHTLY_ATwolno ponownie ustawić dopiero na osobne, jawne polecenie właściciela po odbiorze testów ręcznych.- Końcowe wdrożenie obejmuje backup,
docker compose config --quiet, aktualizację tylko wymaganych usług oraz weryfikacjęps, health, logów i endpointu. Nie używamydown -vani nie usuwamy danych.
Etapy
Etap 1 — katalog workflow i instancje wykonania
Status: zakończony i zweryfikowany 2026-09-21. Fundament wersjonowania i deterministycznego lifecycle'u, nieużywany jeszcze przez produkcyjny driver: niemutowalny katalog workflow (draft → validate → publish, monotoniczne wersje), czysta maszyna stanów queued/running/waiting/succeeded/failed/cancelled, WorkflowInstanceStore z atomowym claimNext(now) i RESUME, migracja SQLite v10 (nowe tabele, bez zmian istniejących). Pełna specyfikacja kontraktów jest w kodzie (domain/ports.ts, infra/sqlite/workflow-catalog*) i testach — nie duplikujemy jej tutaj.
Zweryfikowane gwarancje: najwyżej jedna instancja running globalnie, dowolnie wiele queued/waiting, RESUME zawsze do queued (nigdy do running), event+projekcja commitują się razem, publikacja odrzuca zmienioną/nieważną rewizję. Poza zakresem etapu 1: integracja z driverem/HTTP/UI, graf node'ów, funkcje/akcje/profile modeli.
Etap 2 — graf i interpreter
Status: zakończony 2026-09-21. Graf v2 (schemaVersion: 2, unia obok nieprzezroczystego v1): entryNodeId, jawne przejścia po eventach, typy core emit/wait/subworkflow/forEach/succeed/fail, rozszerzenia przez discriminated union. DAG bez rekursji, jedyna iteracja to sekwencyjny forEach (items zamrożone przy wejściu). Walidator publikacji odrzuca duplikaty, dangling targets, nieosiągalne node'y, cykle (w tym pośrednie, iteracyjnie) i przekroczone limity.
Interpreter jest pure reducerem bez I/O, z trwałym stosem ramek (workflow/forEach/subworkflow, bez rekursji JS). Migracja SQLite v11 dodaje workflow_instance_execution (frame stack, immutable input), append-only workflow_execution_events i node_attempts (attempt token, parent, status). claimNext pod BEGIN IMMEDIATE zapisuje CLAIM + projekcję + execution event + nowy attempt w jednej transakcji; finalizacja poza transakcją wymaga zgodnego expectedSeq/tokenu. Po restarcie tylko wygasła próba running → interrupted; node nie jest nigdy ponawiany po cichu.
Replay: fold ledgeru od immutable inputu odtwarza frame stack/node/sequence; rozbieżność z projekcją to błąd integralności, nie cicha korekta. Porty read-only (getExecutionPosition, listNodeAttempts, getReplayStatus) przygotowują dane pod etap 7, bez endpointów/UI na tym etapie.
Zweryfikowane kryteria odbioru: wielopoziomowy subworkflow/forEach przeżywa restart z identycznym replay; cykle/dangling/limity odrzucone przed publikacją; crash w dowolnym punkcie transakcji nie rozdziela stanu; stale token nie finalizuje nowszej próby; driver pozostaje niepodłączony.
Etap 3 — funkcje i reguły użytkownika
Status: zakończony 2026-09-21.
Wersjonowane funkcje TypeScript/POSIX shell w SQLite, kontrakt {event, summary, data}, obowiązkowe przypadki testowe, capability metadata, port testowego wykonania i deterministyczne fejki. Migracja v12 utrwala drafty, przypadki, niemutowalne wersje i append-only test runs. Node rozszerzenia function wskazuje dokładne functionId@version; snapshot i tworzenie instancji odrzucają brakującą albo zmienioną wersję. Produkcyjny driver nadal nie uruchamia źródła funkcji — izolowany runtime pozostaje etapem 6.
Etap 4 — akcje uprzywilejowane i odwzorowanie aktualizacji
Zamknięty rejestr dozwolonych akcji executora oraz polityki ich użycia. Obecny łańcuch aktualizacji zostaje zapisany jako workflow z zachowaniem wszystkich bramek, kopii, verify i decyzji STOP. Docker image prune może pojawić się dopiero jako jawna akcja z osobną regułą i testami bezpieczeństwa.
Etap 5 — profile modeli i sidecary
Jawne, wersjonowane profile modeli; adaptery Claude i Codex za ModelExecutor, osobne sidecary i brak cichego fallbacku. Profile Codex wystawione przez MCP. Architektura adapterów gotowa na kolejnego dostawcę, np. Gemini.
Etap 6 — izolowany runtime funkcji
Efemeryczne wykonanie funkcji użytkownika z limitami czasu, pamięci i wyjścia, domyślnym brakiem sieci, jawnymi capabilities, maskowaniem oraz pełnymi eventami audytowymi.
Etap 7 — API i edytor webowy
Endpointy katalogu i instancji, Monaco dla funkcji oraz Cytoscape dla grafu. Generyczna zmiana kolejności, przypinanie pozycji, rozwijanie zagnieżdżeń, osobne metadane layoutu i diagnostyka walidacji. Oś czasu aplikacji w stylu Jira pokazuje eventy, przejścia stanów i aktualny node.
Etap 8 — shadow mode i kontrolowane przełączenie
Uruchomienie nowego workflow w trybie dry-run/shadow, porównanie decyzji z obecną kolejką, usunięcie rozbieżności i dopiero potem przejęcie claimowania. Po odbiorze następuje kontrolowane wdrożenie nowego systemu na VM103 z pustym NIGHTLY_AT: usługi mają działać, ale automatyczny przebieg nie może się utworzyć. Właściciel testuje ręcznie. Włączenie nocy, prawdziwe aktualizacje oraz wycofanie starej ścieżki wymagają osobnego odbioru właściciela.
Dziennik etapów
| Etap | Plan Sol | Implementacja Terra | Weryfikacja Sol | Stan |
|---|---|---|---|---|
| 1 | 2026-09-20 | 2026-09-20–21 | PASS 2026-09-21 | zakończony |
| 2 | 2026-09-20 | 2026-09-21 | 1214 testów PASS | zakończony |
| 3 | 2026-09-21 | 2026-09-21 | 1225 testów PASS | zakończony |
| 4–8 | ramowy | realizacja ciągła | audyt przekrojowy przed wdrożeniem | w toku |
Decyzje i zakres — 2026-09-22
Etapy 4-7 powstawały równolegle w kilku sesjach (Codex, Gemini) bez koordynacji; npm run check był czerwony (36 błędów tsc, 132 eslint), mimo że TASK_STATE.md deklarował zieloną walidację. Faza A (ta sesja) przywróciła zielony npm run check bez zmiany zachowania starej ścieżki drivera i bez nowych funkcji. Przy tej okazji właściciel potwierdził wizję i podjął następujące decyzje:
- Wizja: silnik przepływów per aplikacja (przepływ generowany przez Claude), z podglądem na żywo bieżącego kroku, sterowaniem całym runem, dodawaniem kroków/bramek/rozgałęzień, blokami shell/TS i agentem wpinanym w dowolne miejsce procesu; proste edytory skryptów.
- Izolacja runtime'u funkcji pozostaje niepełna świadomie: bez fail-closed na
bwrap, bez egzekwowaniafilesystemRead/Write, bez twardego limitu pamięci dlaposix. Bezpieczeństwo i ograniczenia przyjdą później — na teraz ma być prosto i skutecznie. - Modele: Claude i Codex są pierwszoplanowe; Gemini to tylko gotowość architektury adaptera (
GeminiSidecarAdapter), bez sidecara ani wdrożenia. - Modyfikacja przebiegu = nowa wersja na kolejny run. Trwający run kończy się na swojej wersji (zgodne z niemutowalnością wersji i replayem — patrz Etap 1-2).
- BPMN: ocena gotowej biblioteki BPMN zamiast własnego silnika grafu odbędzie się dopiero po zakończeniu Etapu 8. Do tego czasu zostaje obecny graf (
WorkflowDocumentV2+ Cytoscape).
Luki między wizją a stanem etapów 4-7 (Faza C) — stan 2026-09-23
- Przepływ per aplikacja: zrobione jako kopia szablonu (
update-<app>, seed); nieedytowane przepływy idą za zmianami szablonu (historia skrótów szablonu). Generowanie przez Claude — nie ma. - Start, cancel, resume z API/UI: zrobione (shadow/real, cancel, retry po restarcie).
- Podgląd na żywo: zrobione (graf instancji wycentrowany na bieżącym kroku, SSE).
- Ogólny węzeł „agent”: nie ma.
Faza B — domknięcie poprawności łańcucha aktualizacji (zakończona 2026-09-22)
Zamknęła 5 braków z Fazy A: STOP forbidden_change na unexpected_change w apply_dry_run/apply; prawdziwy retry/healthcheck w verify (reużywa domain/verify.ts); Codex MCP (src/main/codex-mcp-stdio.ts); fail-closed maxCostUsd w model-dispatcher.ts; test HTTP dla .../decisions. Deny-lista kroku migrate/repair nie wymagała zmiany kodu — dziedziczy ochronę z app/executor/agent.ts (nietkniętego przez etapy 4-7). Pełny opis w TASK_STATE.md; commity b02f1aa..88790bc.
Faza D — plan (zatwierdzony przez właściciela 2026-09-23)
Decyzje: model sam wykrywa migracje w release notes i przygotowuje plan (compose, kroki, potrzebne dane); właściciel zatwierdza na bramce i podaje dane w dynamicznym formularzu (sekrety poza ledgerem); zatwierdzony diff compose wchodzi bez deny-listy (nigdy homelab-upgrade). Każdy workflow może mieć cron. Brak listy aplikacji pod absolutnym STOP (decydują enabled/apply).
- A rollback na bramce po kontroli modelu; czytelne bloczki w grafie (ludzka nazwa, rodzaj, opis,
resolve_*przy bramce). - B migracje: operacja
migration_plan(opus, sesjaobserve, schematMigrationPlan), bramkagate_migrationz formularzeminputs(wartości w tabeliworkflow_run_inputs, kasowane po użyciu), akcjadocker.migrate@1w executorze (placeholdery<secret:>/<input:>,.env0600,compose config -q),post_upgradewykonujestepsAfter. - C czyszczenie Dockera: akcje
docker.diskUsage@1,docker.imagePrune@2(unused/until, build cache, nigdy wolumeny),decisionContextdladestructivez runnera, przepływy systemowe (bez aplikacji) startowane z UI, przepływdocker-cleanupzakładany przez UI (funkcjaassess_cleanup, próg 10 GB). - D cron per workflow (tabela
workflow_schedules, parser wdomain/cron.ts, driver co minutę, te same blokady co ręczny start); rozłożenie aktualizacji w tygodniu;docker-cleanup0 6,18 * * *czyści sam przy < 10 GB. - E edytor: formularze per rodzaj węzła zamiast surowego JSON, opis tego, co blok wykona, skąd bierze dane i dokąd prowadzą zdarzenia; dodawanie/usuwanie węzłów.
Kolejność: A → B → C → D → wdrożenie → docker-cleanup przez UI z cronem → E → test migracji na żywo (influx 1.8 → 1.11; migracja 2.x tylko na wyraźne polecenie właściciela, z kopią /srv/appdata/grafana).
Status (2026-09-23): A, B, C, D zrobione (ffed859..a202c1d). Różnice względem planu:
docker.migraterobi całą aktualizację (compose +.env+ przypięty obraz,pull,upcałego projektu) i prowadzi prosto doverify_initial; pobackuprozgałęzia funkcjaapp-update.route-update.rollbackprzywraca też.env.- Polityka aktualizacji ma rewizję 2 (
docker.migrate,docker.diskUsage,docker.imagePrune@2z progiem 10 GB); seed podnosi starszą rewizję.imagePrune@2: dozwolony poniżej progu albo poapprove/proceedwłaściciela tuż przed węzłem. - Harmonogramy sprawdza serwer (co 20 s, raz na minutę dzięki
workflow_schedules.last_minute), strefaSCHEDULE_TZ. docker-cleanupbez bramki: przy ≥ 10 GB wolnego kończy sięterminal_not_needed.- Harmonogramów aktualizacji aplikacji nie ustawiono — decyzja właściciela (kolumna Schedule na liście workflow).
Faza E — uproszczenie dla człowieka (zatwierdzona przez właściciela 2026-09-23)
Decyzje: stary łańcuch kroków usunięty całkiem (z UI i backendu), Codex podłączony przez dispatcher profili, UI i porządki w kodzie w jednym planie.
- 1 Overview jako strona startowa (czeka na Ciebie / trwa / skończone / następne), badge = runy
waiting; „Instances” → „Runs”. - 2 Czytelne runy i bramki: lista z aplikacją, krokiem/wynikiem i filtrami; na stronie runu „Why it failed”; bramka z przyciskiem na akcję i potwierdzeniem rollback/skip.
- 3 Jedno nazewnictwo („Dry run”/„Real”), harmonogram z presetami na stronie aplikacji, potwierdzenia.
- 4 Edytory: „Publish” zapisuje, sprawdza (i testuje funkcję) i publikuje; błędy nie zasłaniają edytora.
- 5 Wycofanie łańcucha: pushe z pętli workflow, statusy Apps z runów,
data_versionbez step-store; usunięte Queue/Runs/Agent, driver kroków, demo, dry-run, watchdog,NIGHTLY_AT/DEMO/DRIVER_IDLE_MS/WATCHDOG_GRACE_MIN. Tabeleruns/steps/reminderszostają nieużywane. - 6 Codex przez
createModelDispatcher. - 7 Porządki bez zmiany funkcji (wymagane porty, podział
http/server.ts, jeden rejestr rozszerzeń, bez fallbacku runtime).
Status (2026-09-23): 1–7 zrobione na foundations; w 7 pominięte podział http/server.ts i przeniesienie walidacji grafu do app/ (przeniesienie, nie uproszczenie). Bez squasha migracji.
- v1 usunięte za zgodą właściciela po odczycie bazy VM103 (brak użyć): dokument/snapshot workflow v1, węzły
function/privilegedActionv1, akcjedocker.inspect/preflight/apply/imagePrune@1, reguła prune v1 (recentBackup). Zostaje projekcja wykonania v1 (jeden zakończony run na VM103 jej używa) i jedyne wersjecomponentOperation/ownerDecisionv1. - Codex: oba sidecary mówią tym samym kontraktem
/run(http/model-server.ts), więc driver ma po jednymcreateHttpModelClientna dostawcę podcreateModelDispatcher; osobne adaptery Claude/Codex/Gemini usunięte (wysyłały pola, które ścisły kontrakt odrzuca). Żądanie bez przypiętego profilu idzie do profilu o nazwie wykonawcy (sonnet/opus), więc przełączenie profilu na Codex na stronie Models kieruje tam modele aplikacji. Profil narzuca limit czasu i kosztu; domyślne profile mają 30 min (seed podnosi nietknięty pierwszy dokument).
Faza F — zaległości bezpieczeństwa, frontend, Arcane (zatwierdzona przez właściciela 2026-09-24)
Decyzje: zostajemy przy własnym silniku (BPMN nie daje gwarancji projektu: jeden węzeł globalnie, snapshot zależności, własna oś czasu); Dockge zastępuje Arcane z compose w git (docs/DESIGN.md, „Arcane i compose w git”). Proste części etapów robi Sonnet w osobnych worktree, trudne i przeglądy sesja prowadząca.
- 0 przegląd UI na żywo — pominięty (brak rozszerzenia Chrome w sesji).
- 1 wartości formularza migracji kasowane po zakończeniu runu (driver przy starcie i po każdym węźle, serwer przy anulowaniu).
- 2 maskowanie sekretów z env i sekretnych pól formularza w wynikach węzłów; wyniki tylko dokładnymi wartościami (heurystyka psułaby
${NAME}w compose planu migracji), błędy także heurystyką. - 3 CLI
codexw obrazie (@openai/codexprzypięty,CODEX_HOME=/codex); logowanie jednorazowe wdocs/DEPLOY.md. - 4 push HA na bramce — kod był; potwierdza właściciel.
- 5 plan migracji zapamiętuje
composeBefore; bramka pokazuje diff compose i ma kolejność: co się zmienia → co mówi model → czego potrzeba → decyzja. - 6 log sesji modelu na żywo: driver co 3 s czyta
/session/log(tabelaworkflow_model_activity, schemat v24), timeline zwracamodelSessions, strona runu pokazuje go na żywo i po fakcie. - 7 Apps: Running/Available/Next run; podgląd wzorca tagów (
POST /api/apps/:app/tag-preview). - 8 widoki na telefonie: nawigacja się zawija, tabele chowają kolumny drugorzędne poniżej 720 px, przyciski decyzji na pełną szerokość, niższy graf.
- 9 Arcane i repo compose: 9a–9e wykonane. Prywatne repo
homelab-composenamasterzawiera 19 projektów i archiwum Dockge; Arcane v2.13.1 ma włączony autosync co 5 minut dla 18 projektów, a executor obsługuje publikację i rollback git orazhl write/up. Dockge zatrzymano, a jego compose/appdata i 29 starych kopii przeniesiono do/srv/appdata/to_delete/20260930; 20 wpisów harmonogramu zapisanych i sprawdzonych przez API. - Podglądy harmonogramów:
nextCronRunsoblicza kandydatów według godziny UTC z jednym formatterem Intl zamiast tworzyć formatter dla każdego kandydata; zachowane są obsługa DST, rozdzielczość 30 minut i strefy takie jak Asia/Kathmandu. Hotfix wdrożono na lokalnym obrazie; przejście na obraz CI wymagaupdate.sh.npm run check: 104 pliki, 1240 testów PASS.
Faza G — nowoczesny, graficzny frontend (zatwierdzona przez właściciela 2026-09-30)
Decyzje: sidebar + karty, ciemny motyw domyślny z przełącznikiem; wykresy w ECharts (rejestrowane tylko potrzebne części, osobny chunk ładowany leniwie); własne okno potwierdzeń, panel boczny i toasty zamiast window.confirm; bez zmian w backendzie — agregacje w przeglądarce z istniejących endpointów (src/web/stats.ts), budowniczki opcji wykresów jako czyste funkcje (src/web/charts/options.ts), kolory z tokenów CSS. Gałąź frontend-refresh.
- G0 powłoka: sidebar z ikonami (na telefonie górny pasek), tokeny powierzchni/cieni,
Card,StatTile,Icon, przełącznik motywu. - G1 okna:
DialogProvider(useConfirm,useToast),Drawer; podgląd runu w panelu z listy Runs. - G2 ECharts i agregacje: runy dziennie wg statusu, wskaźnik sukcesu z okresem poprzednim, czasy trwania, koszt dziennie wg modelu; paleta kategorii sprawdzona pod daltonizm.
- G3 Overview: kafle KPI, aktywność 30 dni, harmonogramy na 7 dni.
- G4 Apps: karty projektów (wersje, 10 ostatnich runów, następny run), przełącznik karty/tabela; strona aplikacji z wykresem czasu trwania runów.
- G5 strona runu: Gantt kroków (klik zaznacza węzeł), historia w oknie.
- G6 Models: koszt dziennie wg modelu, udział modeli, wyniki wg rodzaju pytania; edycja profilu w panelu bocznym.
- G7 porządki stylów, dokumentacja. Przegląd na żywo w przeglądarce jeszcze nie zrobiony (brak przeglądarki na hoście).