# Gema0 — architektura „endgame” (audyt kodu + plan egzekucji) **Źródło prawdy dla tego dokumentu:** wyłącznie kod w repozytorium (Node, Python, JSON config, skrypty `tools/`), **bez** polegania na starszych opisach w `.md`. **Stan repozytorium w momencie audytu:** katalog **`/legacy` nie istnieje** w drzewie plików — nie da się porównać ani zrecyklingować kodu z tej ścieżki, dopóki nie zostanie dodany do repo (poniżej: co zrobić gdy się pojawi). --- ## 1. Jak system **fizycznie** działa dziś (rdzeń) ### 1.1 Warstwa Node (`backend/`) | Komponent | Rola | |-----------|------| | **`server.js` + `loadEnv.js`** | HTTP + WS; **`dotenv`** ładuje **`gema0/.env`** przy starcie (`override: true`). | | **`TaskQueue`** (`services/queue.js`) | Jedna kolejka sekwencyjna (RPA = jeden focus); priorytet; pauza. | | **`rpaDispatcher.runWorker`** | `execa` → Python stdin JSON → stdout końcowy JSON; stderr linie JSONL → log/WS. | | **`enqueuePushToCursor`** (`services/pushTask.js`) | Kolejkowanie **`push_to_cursor.py`** z payloadem (`target_label`, `note_*`, `submit`, **`session_id`**, …). | | **`services/config.js`** | **`GEMA0_NOTES_DIR`** przykrywa `paths.notes`; mapa okien **`backend/config/windows_map.json`**; Gemini z **`config.json`** + env. | ### 1.2 API sterujące - **`POST /api/chat`** (`routes/chat.js`) → `parseCommand` (`commandParser.js`): `PUSH`, `DRY PUSH`, `ZAPISZ`, `HEALTHCHECK`, `ASK GEMINI`, … - **PUSH** → `enqueuePushToCursor` z **`submit: true`** (hardcoded przy typie `push`) — Cursor dostaje **`Ctrl+L` + wklejenie + `Ctrl+Enter`** (domyślnie). ### 1.3 Warstwa Python (`rpa_workers/`) | Worker | Funkcja | |--------|---------| | **`push_to_cursor.py`** | Aktywacja okna (`window_resolver.activate`), blokada inputu (`keyboard_safe`), schowek, sekwencja czatu/hotkeys; **`session_id`** tylko w metadanych/screenshot path — **nie** zabija ani nie tworzy sesji czatu Cursora. | | **`healthcheck.py`**, **`focus_window.py`**, **`clipboard_watcher.py`** | Środowisko / fokus / most schowka (opcjonalny). | ### 1.4 Orkiestracja plików (`tools/`) | Skrypt | Trigger | Zachowanie | |--------|---------|------------| | **`validate-agent-report.mjs`** | CLI / import | Walidacja **nagłówków `##`** raportu, **STEP-ID** w `10`/`20`. | | **`orchestrate-handoff.mjs`** | CLI / przez supervisor | Czyta **`10_*`**, **`20_*`**, opcjonalnie **`git diff/status`** z **`GEMA0_GIT_ROOT`**; jedno **`generateContent`** Gemini; pisze **`30_gemini_brief.md`**; **nadpisuje** **`cmd.md`**; zapisuje **`last_handoff.json`** (hash SHA256 raportu). | | **`supervisor-loop.mjs`** | `chokidar` na **`20_agent_report.md`** + opcjonalny tick retry | Po zmianie + walidacji + niezablokowanym liczniku: `execa` → `orchestrate-handoff`; jeśli `last_handoff` zgadza się z hashem raportu → **`POST /api/chat`** z domyślnym **`PUSH TO … @cmd.md` + `@30_gemini_brief.md`** + aktualizuj `20`. Przy błędzie PUSH zapisuje stan w **`.gema0_state/supervisor_run.json`** (`push_delivery_*`, `push_delivery_last_error`); ponowienia: env `GEMA0_SUPERVISOR_PUSH_RETRY_*` (tick, backoff, opcjonalny limit serii `MAX_ATTEMPTS` + `MAX_EXCEEDED_MODE`). | | **`watch-cmd.mjs`** | `chokidar` na **`cmd.md`** (depth 0) | Osobny proces: debounce → **`fetch` `/api/chat`** z komendą PUSH `@cmd.md`. | ### 1.5 Auto-push (`services/autoPush.js`) - **`chokidar`** na plikach w **`paths.notes`** zgodnie z **`storage/auto_push.json`** (`picomatch`); kolejkuje kolejny PUSH wg reguł; dedupe hash + cooldown. ### 1.6 Integracja Gemini - **`askGemini`** (`geminiClient.js`): jeden model z `@google/generative-ai`, rate limit prostą sliding window. - **Orkiestracja** woła Gemini **bez** osobnego „routera kosztów” — ten sam **`GEMINI_API_KEY` / model** co reszta (nadpisywalne przez env). **Wniosek techniczny:** przepływ to **„pliki na dysku + czasami HTTP + kolejka RPA sterująca UI Cursora”**, a nie zarządzanie wewnętrznym stanem aplikacji Cursor. --- ## 2. Zderzenie z wizją „endgame” ### 2.1 Ekonomiczna kaskada (drogi Supervisor + tani Wyrobnik) | Wymaganie wizji | Stan w kodzie | |-----------------|---------------| | Osobny „najdroższy” model tylko do decyzji | **Brak.** `orchestrate-handoff` używa **jednego** `GEMINI_MODEL` / klucza. Panel **`ASK GEMINI`** — ten sam klient. | | Tani model do kodu w oknach | **Poza repo** — stos modeli Cursora nie jest konfigurowany ani raportowany przez Gema0. | | Routing tokenów / budżet | **Brak** w backendzie. | ### 2.2 „Zabijanie sesji” (zero context drift) | Wymaganie wizji | Stan w kodzie | |-----------------|---------------| | Po każdej akcji: nowa sesja agenta bez historii czatu | **Brak.** `push_to_cursor` wkleja do **tego samego** okna; nie ma wywołania API Cursora ani sekwencji „New Agent / New Chat” (nie ma w Pythonie drugiego hotkeya pod nową sesję). | | `session_id` jako przełącznik sesji | **Nie.** W `push_to_cursor.py` jest używane m.in. do **folderu screenshotów** (`session_id` w ścieżce), **nie** do resetu kontekstu LLM. | | Izolacja pamięci | Częściowo **tylko przez pliki** (`10`/`20`/`30`/`cmd`) — ale **czat Cursora nadal rośnie**, a **`cmd.md` jest dopisywany** (kolejne bloki orchestracji), co jest **akumulacją** treści, nie cięciem. | ### 2.3 Perfekcyjny kontrakt tekstowy | Element | Stan | |---------|------| | Twardy szkielet **`20`** (`##` sekcje, STEP-ID) | **Tak** — `validate-agent-report.mjs`. | | Maszyna-czytelny format poleceń dla nowego agenta | **Częściowo.** `30_gemini_brief.md` to **Markdown z trzema nagłówkami** nakazanymi w `systemInstruction` orchestracji — **nie** jest to JSON/schema z walidacją po stronie Node. | | Jedno źródło prawdy bez „opowieści” | **Słaba strona.** Agent w czacie dostaje PUSH + historia czatu — kontrakt plikowy **nie usuwa** historii czatu ani nie wymusza „tylko plik”. | --- ## 3. `/legacy` W sklonowanym drzewie **brak folderu `/legacy`** — **nie przeprowadzono** eksploracji starszych algorytmów ani porównania z obecną implementacją. **Instrukcja na przyszłość:** gdy **`/legacy`** pojawi się w repo, należy w pierwszej kolejności wyszukać tam: - jakiekolwiek **JSON/schema** komunikatów do agenta - osobny **proces supervise** / kolejki - **watch** na dodatkowych plikach lub **FSM** w jednym pliku stanu - integracje **CLI Cursor** lub **inne kanały** niż czyste UI automation Te fragmenty warto przełożyć na poniższy plan, jeśli nie duplikują obecnych `tools/`. --- ## 4. Docelowa architektura (propozycja techniczna) Założenia zgodne z wizją: **minimalizacja kontekstu w czacie**, **maksymalizacja deterministycznego stanu na dysku**, **rozdzielenie „mózg decyzyjny” vs „peryferia kodująca”**. ### 4.1 Warstwa stanu — maszyna cyklów (nowy artefakt) Wprowadzić **`runtime_state.json`** (lub **`00_machine_state.yaml`**) pod **`.gema0/.gema0_state/`** z polami minimalnymi: - `cycle_id` (UUID / monotonic) - `phase`: `WAIT_REPORT` \| `SUPERVISING` \| `WAIT_WORKER_ACK` \| `FAILED` \| `DONE` - `supervisor_model` vs `policy_version` - **`worker_chat_fingerprint`** (np. licznik: każdy cykl Worker = świeży chat) **Invariant:** żaden PUSH do Worker nie następuje, jeśli `phase` nie pozwala (ochrona przed przełączeniami). ### 4.2 Kontrakt maszynowy dla Wyrobnika (nowy format) Zamiast tylko markdown-brief dla ludzi: - **`11_orders/ORDER-.json`** (albo pojedynczy **`11_worker_slot.md`** z **YAML front matter** walidowanym przez `ajv`/Zod w Node): ```yaml cycle_id: "…" references: ["10_step_current.md", "GIT_DIFF_HINT"] constraints: [] commands: - type: EDIT_FILE path: "src/foo.ts" directive: "…" artifacts_required: - "20_agent_report.md" ``` Walidacja **przed PUSH** eliminuje „gadanie” w treści dostarczonej przez Supervisor. Supervisor (**Gemini albo OpenAI**) musi dostarczyć **JSON** mieszczący się w schemacie; warstwa Node robi **`JSON.parse` + walidacja** i dopiero wtedy montuje PUSH z **jednym** blokiem dla pliku poleceń workera (np. `@11_worker_slot.md`), bez dopisywania starych instrukcji do `cmd.md`. ### 4.3 Zabijanie sesji Cursora — trzy realizacje do wyboru (rosnący koszt wdrożenia) **Opcja A — UI RPA „Nowy agent / nowy czat”** - Rozszerzyć `keyboard_safe` / `push_to_cursor` o konfiguralną **sekwencję prefiksu**: np. **`Ctrl+N`** / skrót z ustawień Cursora („New Agent”). - W dokumentacji **`config.json`**: osobny obszar `cursor.session_reset_send_keys`. - Wymóg: ustalić **stabilnie** działający skrót dla danej wersji Cursora (test w `healthcheck`). **Opcja B — rotacja `CURSOR_1` → `CURSOR_2` (osobna instancja/okno na cykl)** - Pula okien Workera; Supervisor wybiera kolejne wolne okno / etykietę. - Wymóg: ludzki lub skryptowany start wielu okien lub profili. **Opcja C — Cursor Agent przez CLI/API (preferowana długofalowo, jeśli dostępne)** - Zamiast wklejania do UI: **`cursor agent`** lub oficjalne API — kontekst tylko z wybranych plików, bez narastającej historii wizualnego czatu. - Wymóg: dostępność stabilnego CLI w środowisku użytkownika. Bez którejś z powyższych opcji wizja **„zero drift w czacie”** nie jest w pełni spełniona przy obecnym RPA. ### 4.4 Ekonomiczna kaskada LLM — minimalna zmiana w kodzie Gema0 1. Rozdziel zmienne środowiskowe: **`GEMA0_SUPERVISOR_API_KEY`**, **`GEMA0_SUPERVISOR_MODEL`**, oraz opcjonalnie osobne zmienne dla tanich dialogów operatorskich w panelu. 2. W `orchestrate-handoff.mjs`: użyć wartości „supervisory”; ewentualnie **krótki extraction pass** tanim modelem (`flash-lite`) który dostarcza tylko `git_files[]` / `risks[]` dla drugiego wywołania (kaskada dwustopniowa, tańsza niż wysłanie całego diffu do reasoning modelu). 3. **Panel** **`ASK GEMINI`** może pozostać na tanim modelu — nie mieszać z orchestracją. ### 4.5 `cmd.md` — polityka no-append - Zamiast **append** HTML-komentarzy: **`cmd.md` nadpisywany atomowo** szablonem z **ostatniego** `cycle_id` + link do `30`/`11` + jedna linia „tylko czytaj te pliki”. - Archiwum: **`cmd_history/YYYY-MM-DD-.md`** (opcjonalnie). ### 4.6 Observability - Każdy cykl: jeden wiersz w **`40_session_log.md`** lub JSONL **`events.jsonl`** z `cycle_id`, hash `20`, czas, model, outcome. - Metryki: liczba tokenów **po stronie Gemini** (`usageMetadata` z API, jeśli SDK zwraca) — dziś nie zbierane. --- ## 5. Plan egzekucji (krok po kroku w kodzie) ### Faza 0 — wejściowe - [ ] **Dodać `/legacy`** do repo lub spakowany archiwum i wykonać **osobny diff** przeciwko `tools/supervisor-loop.mjs` / `backend` (uzupełnienie sekcji 3 tego dokumentu). ### Faza 1 — twardy kontrakt i stan 1. [ ] Nowy moduł Node: **`backend/services/orderSchema.mjs`** (Zod): schema `WorkerOrder`. 2. [ ] Rozszerzyć `orchestrate-handoff.mjs`: - tryb **`--json-out`** / flaga `GEMA0_ORCHESTRATE_FORMAT=json` - **retry** parsowania + naprawa przez ten sam model z „fix this JSON” (ograniczone próby) 3. [ ] Dodać **`runtime_state.json`** + mały **`stateMachine.mjs`** (tylko `fs` + lock plikowy). 4. [ ] **`supervisor-loop.mjs`**: przed `execa` orchestracji sprawdzać `phase`; po sukcesie ustawiać `WAIT_WORKER_ACK`. ### Faza 2 — reset sesji (MVP) 1. [ ] **`config.json`**: `cursor.session_reset_send_keys` (string jak `open_chat_send_keys`). 2. [ ] **`push_to_cursor.py`**: nowy krok opcjonalny **`reset_session: true`** w payload → sekwencja przed `open_chat`. 3. [ ] **`healthcheck.py`**: opcjonalny test „czy skrót otwiera nową sesję” (heurystyka: tytuł panelu / screenshot diff — uproszczony smoke). ### Faza 3 — kaskada kosztów 1. [ ] Rozdzielenie env dla supervisor vs panel. 2. [ ] Opcjonalny **pre-pass** Gemini Flash na skróconym diffie (implementacja funkcji **`summarizeGitForSupervisor(gitRoot)`** w Node). ### Faza 4 — czystkość dysku i audyt 1. [ ] Zmiana **`append`** `cmd.md` → **atomic replace** + archiwum. 2. [ ] Jednolity log zdarzeń JSONL dla panelu Workflow. --- ## 6. Podsumowanie jednym zdaniem Obecny Gema0 to **solidna automatyka plików + RPA czatu Cursora pod jednym modelem Gemini**; wizja **endgame** wymaga trzech filarów kodu, których dziś brakuje: **(a)** jawnego resetu lub zamiennika sesji Workera, **(b)** **deterministycznego formatu poleceń** (JSON lub schema z walidacją po stronie Node), **(c)** **rozdziału kosztowych ról LLM**. Katalog **`/legacy`** trzeba dodać do repozytorium, żeby móc ocenić realny recycling starej logiki.