feat: initial commit

This commit is contained in:
2026-08-29 13:17:59 +02:00
commit 142f5f5759
91 changed files with 6155 additions and 0 deletions
@@ -0,0 +1,45 @@
# ADR-0001: APM jako łańcuch dostaw kontekstu agentowego
- Status: przyjęty
- Data: 2026-08-28
## Kontekst
Prompty, skille i guardraile dla agentów mają wszystkie cechy zależności: są współdzielone
między repozytoriami, zmieniają się w czasie, mają właścicieli i wpływają na wynik.
Trzymane jako pliki w repozytorium silnika stają się nierozróżnialne od kodu: nie da się ich
niezależnie wersjonować, nie da się powiedzieć "ten przebieg użył wiedzy migracyjnej w wersji 1.2.0".
## Decyzja
Kontekst agentowy dostarczamy pakietami APM (Microsoft Agent Package Manager):
`apm.yml` deklaruje zależności, `apm install` rozwiązuje je do `apm_modules/`,
`apm.lock.yaml` pinuje commity i hashe, `apm-policy.yml` ogranicza dopuszczalne źródła
i prymitywy, `apm audit` wykrywa dryf.
Silnik (Python + agno) czyta prymitywy z `.apm/` oraz `apm_modules/` i kompiluje je
do obiektów agno. Prymitywy lokalne mają pierwszeństwo przed zainstalowanymi.
## Konsekwencje
**Pozytywne**
- Zespół właściciela SDK dostarcza wiedzę migracyjną jako pakiet, bez dostępu do silnika.
- Hash lockfile'a w manifeście przebiegu odpowiada na pytanie "co dokładnie wiedział model".
- Polityka instalacyjna jest egzekwowana w CI, a nie w regulaminie.
**Negatywne**
- Dodatkowa zależność narzędziowa (APM CLI) w obrazie runnera.
- Potrzebny wewnętrzny rejestr pakietów (w praktyce: repozytoria GitLaba) i dyscyplina tagowania.
- Ryzyko rozjazdu między wersją silnika a formatem prymitywów - łagodzone testem
`test_apm_context.py`, który waliduje kontekst przy każdym MR.
## Rozważane alternatywy
- **Prompty w repozytorium silnika.** Najprostsze, ale uniemożliwia niezależne wersjonowanie
wiedzy dziedzinowej i rozmywa własność.
- **Baza wektorowa z dokumentacją migracji.** Nieodtwarzalne: dwa przebiegi na tym samym commicie
mogą dostać inny kontekst. Odpada w środowisku wymagającym audytu.
- **Własny format pakietu.** Koszt utrzymania bez zysku; APM daje lockfile, politykę i skanowanie
ukrytego Unicode z pudełka, opierając się na otwartych standardach (AGENTS.md, Agent Skills, MCP).
@@ -0,0 +1,39 @@
# ADR-0002: Deterministycznie tyle, ile się da; model tylko na resztę
- Status: przyjęty
- Data: 2026-08-28
## Kontekst
Migracja SDK to w większości mechaniczne podmiany. Powierzenie ich modelowi kosztuje tokeny,
wydłuża przebieg i wprowadza wariancję tam, gdzie wariancja jest czystą stratą -
nikt nie chce, żeby dwa uruchomienia dawały inny diff dla tej samej zmiany nazwy metody.
## Decyzja
Dzielimy pracę wzdłuż linii "czy da się to zrobić kodem":
1. Wykrycie ekosystemu, plików zależności i komendy testowej - adapter (`adapters/`).
2. Podbicie deklaracji wersji - adapter.
3. Migracja objęta regułami - silnik reguł czytający `*.codemod.yaml` z pakietu APM.
4. Reszta - agent `coder` z LLM.
5. Ocena wyniku - agent `reviewer` **oraz** niezależna kontrola mechaniczna.
Reguły codemod są częścią pakietu APM (leżą obok notatki migracyjnej), więc podlegają
temu samemu przeglądowi i wersjonowaniu co wiedza dla modelu.
## Konsekwencje
**Pozytywne**
- Powstaje tryb `--offline`: ta sama topologia bez modelu. Smoke test całego pipeline'u
na każdym MR, bez GPU i bez internetu.
- Punkt odniesienia: widać, ile przypadków model faktycznie dołożył ponad reguły.
- Mniejszy koszt i krótszy przebieg dla typowych migracji.
**Negatywne**
- Dwie ścieżki do utrzymania (`RuleBrain`, `LlmBrain`) i wspólny kontrakt między nimi.
- Reguły regexowe mają znane ograniczenia (zmiany strukturalne, wieloliniowe konteksty).
Świadomie nie budujemy własnego silnika AST - od tego są narzędzia dziedzinowe
(OpenRewrite, jscodeshift), które można podpiąć jako kolejny adapter.
@@ -0,0 +1,38 @@
# ADR-0003: Runtime w efemerycznym jobie GitLab CI, nie w usłudze
- Status: przyjęty
- Data: 2026-08-28
## Kontekst
agno pozwala wystawić agentów jako usługę (AgentOS/FastAPI) z bazą sesji, pamięcią i UI.
Kuszące, ale dla zadania "zmodyfikuj kod i otwórz MR" oznacza nową usługę produkcyjną:
własne SLA, uwierzytelnianie, przechowywanie stanu, przegląd bezpieczeństwa i dostęp
do repozytoriów z długożyjącego procesu.
## Decyzja
Runtime to efemeryczny job GitLab CI. Jedno uruchomienie = jeden job = jeden katalog roboczy
= jeden komplet artefaktów. Stan przebiegu żyje w artefaktach (`run.json`, `trace.jsonl`,
`changes.patch`), nie w bazie. Uprawnienia to uprawnienia joba (token projektowy),
a nie konta usługowego z dostępem do wszystkiego.
Wyzwalanie: issue z labelką (webhook → trigger token), zdarzenie na MR, harmonogram
oraz ręczny formularz w "Run pipeline" (zmienne z `description` i `options`).
## Konsekwencje
**Pozytywne**
- Zero nowej usługi do utrzymania i przeglądu bezpieczeństwa.
- Naturalna izolacja: brak stanu współdzielonego między przebiegami.
- Bramka manualna w CI daje właściciela podpisu pod zmianą.
- Limity zasobów i czasu przychodzą z platformy CI.
**Negatywne**
- Brak pamięci między przebiegami - powtórka zaczyna od zera (świadomy kompromis:
pamięć między przebiegami w narzędziu modyfikującym kod to więcej ryzyka niż korzyści).
- Zimny start: instalacja kontekstu APM w każdym przebiegu (łagodzone cache'em i artefaktami).
- Brak interaktywnego trybu "zapytaj człowieka w trakcie" - stąd wzorzec `requires_human`:
agent nie pyta, tylko oznacza i idzie dalej.
@@ -0,0 +1,36 @@
# ADR-0004: Guardraile w dwóch warstwach - prompt nie jest zabezpieczeniem
- Status: przyjęty
- Data: 2026-08-28
## Kontekst
Instrukcje w promptcie ("nie modyfikuj .gitlab-ci.yml") działają w większości przypadków,
ale są miękkie: zależą od modelu, długości kontekstu i sformułowania zadania.
W repozytorium bankowym pytanie audytora nie brzmi "czy model zwykle tego nie robi",
tylko "co się stanie, jeśli spróbuje".
## Decyzja
Każdy istotny guardrail istnieje dwukrotnie:
- jako instrukcja APM dla modelu (`security.instructions.md`) - żeby agent w ogóle nie próbował,
- jako mechanizm w kodzie (`WorkspaceTools`, `Settings.deny_globs`, budżety, brak powłoki) -
żeby próba zakończyła się błędem narzędzia zapisanym w śladzie audytowym.
Ta sama zasada dotyczy oceny wyniku: werdykt agenta `reviewer` jest uzupełniany niezależną
kontrolą mechaniczną (pliki objęte zakazem, zmiany w testach, budżet zakresu, status weryfikacji).
## Konsekwencje
**Pozytywne**
- Naruszenie guardraila jest zdarzeniem obserwowalnym, a nie niewidoczną zmianą w diffie.
- Da się odpowiedzieć na pytanie "co system uniemożliwia", a nie tylko "o co prosi".
- Testy zabezpieczeń (`test_workspace_tools.py`) są testami jednostkowymi, nie ćwiczeniem z promptowania.
**Negatywne**
- Duplikacja reguł w dwóch miejscach - trzeba je świadomie utrzymywać razem.
- Zbyt ciasne budżety potrafią zablokować poprawną, ale szeroką zmianę; wartości są
konfigurowalne przez zmienne środowiskowe i powinny być dostrajane per klasa repozytoriów.
@@ -0,0 +1,73 @@
# ADR-0005: Backend LLM wybierany per platforma, atrapa jako test double
- Status: przyjęty
- Data: 2026-08-29
## Kontekst
Docelowo pipeline korzysta z vLLM wystawionego na OpenShifcie. Do pracy nad samym
pipeline'em potrzebny jest jednak model uruchamialny lokalnie - i tu kończy się
jednorodność: **nie istnieje jedna komenda uruchamiająca vLLM na Linuksie i na macOS**.
- Linux z GPU NVIDIA: kontener `vllm/vllm-openai`, ścieżka najbliższa produkcji.
- macOS na Apple Silicon: Docker nie ma dostępu do Metala, więc kontener odpada.
Zostaje natywny plugin społecznościowy `vllm-project/vllm-metal` (backend MLX),
instalowany skryptem do własnego venva, wymagający arm64 Pythona 3.12 i Xcode CLT.
- Maszyna bez GPU i bez Apple Silicon: żaden z powyższych nie ma sensu.
Osobny problem: testy. Tryb `--offline` sprawdza `RuleBrain`, ale nie dotyka tego,
co jest w tym projekcie najbardziej kruche - budowy agentów z definicji APM,
wywoływania narzędzi przez model i parsowania strukturalnych wyjść. Uzależnienie
tego testu od GPU oznaczałoby, że nie uruchomi go nikt poza jedną maszyną.
## Decyzja
**Kontrakt jest jeden: endpoint zgodny z API OpenAI pod `CODEMOD_LLM_BASE_URL`.**
Backendy różnią się wyłącznie tym, jak ten endpoint powstaje. Konfiguracja mieszka
w `llm/models.yml` (jedno źródło prawdy: adresy, modele per rola, argumenty serwera),
a `task llm:up` wykrywa platformę i uruchamia właściwy wariant:
| Backend | Kiedy | Jak |
|---|---|---|
| `vllm-gpu` | Linux + NVIDIA + Docker | `docker compose --profile vllm-gpu` |
| `vllm-metal` | macOS Apple Silicon | natywny plugin MLX, `vllm serve` |
| `ollama` | awaryjnie, obie platformy | natywnie (Metal) lub kontener |
| `mock` | testy, CI | `llm/mock_server.py`, stdlib, bez pobierania |
Atrapa (`mock`) to **test double, nie symulator modelu**: odpowiada z góry ustalonymi
wywołaniami narzędzi i strukturami zestrojonymi z fixture'em `acme-app`, a routing
odpowiedzi opiera na nazwie agenta wstrzykiwanej przez agno do komunikatu systemowego.
Dzięki niej `tests/test_llm_path.py` przepuszcza pełny przebieg przez `LlmBrain`
na każdym MR, bez GPU i bez sieci.
## Konsekwencje
**Pozytywne**
- Projekt startuje od zera na Linuksie i na macOS jedną komendą; różnice platformowe
są schowane za `task llm:up`, a nie rozsypane po README.
- Ścieżka agentowa jest testowana automatycznie, nie tylko ręcznie na czyjejś maszynie.
- Podmiana modelu lub backendu to zmiana w `llm/models.yml`, nie w kodzie.
- Wszystkie backendy wymuszają `--enable-auto-tool-choice` z parserem `hermes`;
bez tego vLLM zwraca opis wywołania w treści zamiast `tool_calls` i agent `coder`
nie tknąłby żadnego pliku.
**Negatywne**
- Cztery ścieżki uruchomieniowe do utrzymania. Ograniczamy koszt tym, że wiedza
o nich jest w jednym pliku YAML plus jednym skrypcie, a nie w zadaniach.
- `vllm-metal` jest pluginem społecznościowym - może się rozjechać z upstreamem vLLM.
Dlatego Ollama zostaje jako wariant awaryjny na macOS.
- Atrapa jest zestrojona z fixture'em: zmiana fixture'u wymaga aktualizacji scenariusza.
To świadomy koszt - alternatywą jest brak testu tej ścieżki.
- Model 4B wystarcza do sprawdzenia hydrauliki, ale nie do realnych migracji.
Do pracy trzeba większego - stąd tabela rekomendacji per rola w `llm/models.yml`.
## Rozważane alternatywy
- **Tylko vLLM, macOS niech używa zdalnego endpointu.** Odpada: uniemożliwia pracę
offline i uzależnia każdego developera od środowiska współdzielonego.
- **Tylko Ollama.** Prostsze, ale rozjeżdża się z produkcją (inne API tool-callingu,
inne kwantyzacje), więc testy przestałyby cokolwiek mówić o zachowaniu na vLLM.
- **Brak atrapy, testy tylko na realnym modelu.** Niedeterministyczne i niedostępne
w CI; test, który czasem przechodzi, jest gorszy niż brak testu.
+175
View File
@@ -0,0 +1,175 @@
# Architektura sieci agentowej modyfikującej kod
## 1. Problem
Zmiany typu "podnieś wersję SDK w 60 repozytoriach" są mechanicznie proste, ale kosztowne:
każde repozytorium ma inny układ, inne miejsca użycia biblioteki i inny poziom pokrycia testami.
Klasyczna automatyzacja (skrypt + sed) załatwia 70% przypadków i zostawia najgorsze 30%.
Agent z LLM załatwia pozostałe 30%, ale wprowadza trzy nowe problemy: niepowtarzalność,
brak audytowalności i nieograniczony zakres zmian.
Ten projekt jest odpowiedzią na pytanie: **jak wpuścić agenta do repozytorium tak,
żeby dało się to pokazać audytorowi.**
## 2. Trzy warstwy i jedna zasada
```
┌───────────────────────────────────────────────────────────────────┐
│ KONTEKST — APM (Microsoft Agent Package Manager) │
│ apm.yml + .apm/{instructions,skills,prompts,agents,context} │
│ Wersjonowany, pinowany lockfile'em, audytowany polityką. │
└──────────────────────────────┬────────────────────────────────────┘
│ apm install → apm_modules/
┌──────────────────────────────▼────────────────────────────────────┐
│ RUNTIME — agno Workflow │
│ Kompilacja prymitywów APM → Agent/Skills/Steps. │
│ Deterministyczne kroki + sandboxowane narzędzia. │
└──────────────────────────────┬────────────────────────────────────┘
│ agentic-codemod run
┌──────────────────────────────▼────────────────────────────────────┐
│ EGZEKUCJA — GitLab CI │
│ Efemeryczny job, bramka manualna, artefakty audytowe, MR. │
└───────────────────────────────────────────────────────────────────┘
```
Zasada nadrzędna: **kontekst jest artefaktem, nie kodem**. Zmiana zachowania sieci agentowej
(nowa reguła migracji, ostrzejszy guardrail, inny model dla roli) to podbicie wersji pakietu APM,
a nie merge request do Pythona. Dzięki temu zespół właściciela SDK może dostarczyć wiedzę
migracyjną, nie mając dostępu do silnika pipeline'u.
## 3. Przepływ
```mermaid
flowchart TD
T[Trigger: issue z labelką / MR / harmonogram / formularz] --> C[apm install + apm audit]
C --> I[intake: deterministyczny wsad zadania]
I --> R[recon: fakty o repozytorium]
R --> P[plan: ChangePlan z kryteriami akceptacji]
P --> G{jest co wdrażać?}
G -- nie --> M[manifest + komentarz w issue]
G -- tak --> B[bump wersji: deterministyczny]
B --> L[pętla: implement → verify]
L -- czerwono, iteracja < limit --> L
L -- zielono --> V[review: recenzja diffa]
V --> S[scribe: opis merge requesta]
S --> H{bramka manualna w CI}
H -- zatwierdzone --> MR[branch + commit + push + MR]
H -- nie --> A[artefakty do wglądu, zero zmian zdalnych]
```
### Role w sieci
| Agent | Model | Narzędzia | Wyjście |
|---|---|---|---|
| `scout` | planner | odczyt, wyszukiwanie | `RepoProfile` |
| `planner` | planner | odczyt, wyszukiwanie | `ChangePlan` |
| `coder` | coder | odczyt, edycja, weryfikacja | zmieniony kod |
| `reviewer` | reviewer | odczyt, diff | `ReviewVerdict` |
| `scribe` | scribe | diff | `MergeRequestDraft` |
Definicje ról leżą w `.apm/agents/*.agent.md`. Kod nie zna nazw agentów - czyta je z kontekstu.
## 4. Co jest deterministyczne, a co należy do modelu
To jest najważniejsza decyzja projektowa. Model dostaje wyłącznie tę część pracy,
której nie da się zrobić inaczej.
| Krok | Wykonawca | Dlaczego |
|---|---|---|
| Wykrycie ekosystemu, plików zależności, komendy testowej | kod (`adapters/`) | jednoznaczne, sprawdzalne |
| Podbicie deklaracji wersji | kod (`adapters/`) | zero powodów, by ryzykować halucynację w pliku zależności |
| Migracja objęta regułami codemod | kod (`workflow/codemod.py`) | reguły z pakietu APM, w pełni powtarzalne |
| Nietypowe użycia API, kontekst biznesowy | model (`coder`) | tu klasyczna automatyzacja się kończy |
| Ocena zakresu i ryzyka zmiany | model (`reviewer`) + kontrola mechaniczna | dwa niezależne spojrzenia |
| Uruchomienie testów | kod (`tools/verification.py`) | agent nie dostaje powłoki |
| Branch, commit, push, MR | kod (`workflow/runner.py`) | jednoznaczny autor i format historii |
Konsekwencja: pipeline ma **dwa tryby o identycznej topologii**. Tryb `--offline` używa
wyłącznie reguł (`RuleBrain`), tryb domyślny dokłada agentów (`LlmBrain`). Tryb offline jest
bramką jakości na każdym MR - sprawdza cały przepływ bez kosztu GPU i bez dostępu do modelu.
## 5. Model bezpieczeństwa
Guardraile istnieją w dwóch warstwach, bo prompt nie jest zabezpieczeniem.
**Warstwa promptowa** (`.apm/instructions/security.instructions.md`) - dla modelu:
zakaz zmian w plikach pipeline'u, zakaz sekretów, zakaz rozszerzania zakresu,
zakaz usuwania testów, zakaz nowych zależności.
**Warstwa egzekucji** (`tools/workspace.py`, `config.py`) - dla audytora:
- każda ścieżka rozwiązywana względem korzenia repozytorium; wyjście przez `..` i dowiązania niemożliwe,
- lista `DEFAULT_DENY_GLOBS` blokuje zapis do `.gitlab-ci.yml`, `.git/`, `Dockerfile*`, `*.pem`, `.env*`, manifestów APM,
- budżety: liczba zmienionych plików, rozmiar pliku, liczba wywołań narzędzi, limit czasu weryfikacji,
- agent nie ma powłoki - jedyna operacja wykonawcza to `run_verification` z komendą ustaloną przez adapter,
- środowisko weryfikacji jest czyszczone ze zmiennych zawierających `TOKEN`, `SECRET`, `PASSWORD`, `API_KEY`,
- każde wywołanie narzędzia trafia do `trace.jsonl` z redakcją sekretów.
**Warstwa dostawy kontekstu** (`apm-policy.yml`): dozwolone źródła pakietów, zakaz prymitywu
`hooks` (wykonuje kod na runnerze), wymagane piny po tagu, wymagany lockfile, `apm audit`
wykrywający ręczną edycję zainstalowanego kontekstu.
## 6. Artefakty przebiegu
Każde uruchomienie zostawia w `--run-dir` komplet dowodów:
| Plik | Zawartość |
|---|---|
| `run.json` | manifest: zadanie, profil, plan, weryfikacje, werdykt, modele, hash lockfile'a APM |
| `plan.json` | plan zmian z kryteriami akceptacji i pozycjami `requires_human` |
| `profile.json` | ustalone fakty o repozytorium |
| `review.json` | werdykt recenzenta z findingami |
| `merge_request.md` | tytuł i opis MR |
| `changes.patch` | pełny diff |
| `trace.jsonl` | każde wywołanie narzędzia z czasem i wynikiem |
To jest odpowiedź na pytanie audytora "na jakiej podstawie ta zmiana weszła do repozytorium".
## 7. Backend LLM
Silnik zna wyłącznie jeden kontrakt: **endpoint zgodny z API OpenAI pod `CODEMOD_LLM_BASE_URL`**.
Cała wiedza o dostawcy jest w `llm.py` (fabryka modeli) i `llm/models.yml` (adresy, modele per rola).
Docelowo jest to vLLM na OpenShifcie. Lokalnie nie ma jednej ścieżki dla obu systemów -
Docker na macOS nie ma dostępu do Metala - więc `task llm:up` wykrywa platformę:
| Backend | Kiedy | Mechanizm |
|---|---|---|
| `vllm-gpu` | Linux + NVIDIA + Docker | `vllm/vllm-openai` przez docker compose |
| `vllm-metal` | macOS Apple Silicon | plugin `vllm-project/vllm-metal` (MLX), natywny `vllm serve` |
| `ollama` | awaryjnie, obie platformy | `/v1` zgodne z OpenAI, natywnie lub w kontenerze |
| `mock` | testy i CI | `llm/mock_server.py` - stdlib, zero pobierania |
Każdy backend serwujący realny model wymusza `--enable-auto-tool-choice` z parserem `hermes`.
Bez tego vLLM zwraca opis wywołania w treści odpowiedzi zamiast `tool_calls`, agent `coder`
nie tknąłby żadnego pliku, a przebieg kończyłby się "sukcesem" bez jednej zmiany - awaria cicha,
czyli najgorszy rodzaj.
### Atrapa jako test double
`mock` nie jest symulatorem modelu. Odpowiada z góry ustalonymi wywołaniami narzędzi
i strukturami zestrojonymi z fixture'em `acme-app`, a rozpoznaje pytającego po nazwie agenta,
którą agno wstrzykuje do komunikatu systemowego. Dzięki temu `tests/test_llm_path.py`
przepuszcza pełny przebieg przez `LlmBrain` - budowę agentów z definicji APM, tool-calling,
parsowanie strukturalnych wyjść i pętlę weryfikacji - na każdym MR, bez GPU i bez sieci.
Podział odpowiedzialności testów jest więc taki:
| Co jest sprawdzane | Czym |
|---|---|
| reguły, adaptery, sandbox, workflow | tryb `--offline` (`RuleBrain`) |
| agenci, narzędzia, schematy, pętla | tryb `llm` na atrapie (`LlmBrain` + `mock`) |
| jakość migracji na nietypowym kodzie | realny model, ręcznie |
## 8. Granice rozwiązania
- **Bez agenta-nadzorcy.** Topologia jest jawnym przepływem (agno `Workflow`), a nie
swobodną delegacją między agentami. W zamian za mniejszą elastyczność dostajemy
przewidywalny koszt, powtarzalny ślad i możliwość wstawienia bramki między krokami.
- **Bez AgentOS.** Runtime to efemeryczny job CI. Nie utrzymujemy usługi z pamięcią sesji -
stan przebiegu żyje w artefaktach.
- **Bez dostępu do sieci z poziomu agenta.** Cała wiedza migracyjna przychodzi w pakiecie APM.
Jeśli notatki migracyjnej nie ma, pipeline zatrzymuje się i zgłasza `requires_human`,
zamiast migrować "z pamięci modelu".
- **Nie każde repozytorium się nadaje.** Bez działającej komendy testowej weryfikacja nic nie znaczy,
a bez weryfikacji ten pipeline nie ma prawa niczego publikować.
+178
View File
@@ -0,0 +1,178 @@
# Runbook
## Uruchomienie lokalne
Zadania uruchamia [go-task](https://taskfile.dev): `brew install go-task` na macOS,
`pip install go-task-bin` gdziekolwiek indziej.
```bash
task doctor # co jest w środowisku, czego brakuje
task install # .venv + zależności (runtime i dev)
task test # 46 testów, bez LLM
task demo # pełny przebieg offline na fixture
task ci # lint + test + demo, czyli to co sprawdza pipeline
```
Wynik przebiegu: `.runs/demo/run.json`, `changes.patch`, `merge_request.md`, `trace.jsonl`.
### macOS - trzy rzeczy, które potrafią zaskoczyć
1. **`externally-managed-environment` (PEP 668).** Homebrew blokuje `pip install` do interpretera
systemowego. Dlatego `task install` zawsze tworzy `.venv` i instaluje tylko do niego.
Nigdy nie uruchamiaj `pip install -e .` na `python3` z systemu.
2. **Systemowy Python to 3.9.** Za stary. `brew install python@3.12`, potem
`PYTHON=python3.12 task install` - `task doctor` powie, jaka wersja jest widoczna.
3. **Weryfikacja repozytorium uruchamia się interpreterem z `.venv`** (`sys.executable`),
a nie `python3` z `PATH`. Repozytorium docelowe z własnym środowiskiem budowania
nadpisuje komendę przez `--verify-command`.
## Model językowy lokalnie
```bash
task llm:list # backendy i ten wykryty dla tej maszyny
task llm:up # uruchomienie + czekanie na gotowość endpointu
task llm:status # czy odpowiada i jakie modele wystawia
task llm:smoke # jedno zapytanie kontrolne
task llm:down
```
### Logi
Jedno wejście niezależnie od tego, czy backend siedzi w kontenerze (`vllm-gpu`),
czy jest procesem z plikiem logu (`vllm-metal`, `ollama`, `mock`):
```bash
task llm:logs # na żywo, ostatnie 100 linii kontekstu
task llm:logs TAIL=500 # więcej historii
task llm:logs FOLLOW=false # jednorazowy zrzut zamiast śledzenia
task llm:logs GREP=throughput # filtr (wyrażenie regularne, bez rozróżniania wielkości liter)
task llm:logs:errors # tylko błędy i ostrzeżenia
task llm:logs:save OUT=/tmp/vllm.log # zrzut do pliku, np. do zgłoszenia
```
Ctrl-C kończy śledzenie normalnie. Pliki logów żyją w `.runs/llm/`.
Czego szukać w logach vLLM:
| Linia | Znaczenie |
|---|---|
| `Downloading ... safetensors` | pobieranie modelu; przy pierwszym starcie to zwykle najdłuższy etap |
| `Application startup complete` | serwer gotowy, endpoint odpowiada |
| `Avg prompt throughput ... Avg generation throughput` | przebieg pracuje; zera przez dłuższą chwilę oznaczają, że nikt nie pyta |
| `GPU KV cache usage` | rosnące do 100% zwiastuje kolejkowanie żądań |
| `CUDA out of memory` | zmniejsz `--max-model-len` albo `VLLM_GPU_UTIL`, albo weź mniejszy model |
| `ValueError: ... tool call parser` | zła wartość `--tool-call-parser` dla tego modelu |
Przy atrapie (`mock`) log pokazuje przebieg rozmowy z agentami - po jednej linii
na żądanie, z nazwą roli i wywoływanym narzędziem. To najszybszy sposób, żeby
zobaczyć, w którym kroku pipeline utknął.
| Backend | Warunki | Uwagi |
|---|---|---|
| `vllm-gpu` | Linux, GPU NVIDIA, Docker | pierwsze uruchomienie pobiera model (kilka GB), `start_period` w healthchecku to 5 minut |
| `vllm-metal` | macOS Apple Silicon, arm64 Python 3.12, Xcode CLT | plugin instaluje się do `~/.venv-vllm-metal` przy pierwszym `task llm:up` |
| `ollama` | `ollama` w PATH albo Docker | wariant awaryjny; na macOS instaluj natywnie, w kontenerze liczy na CPU |
| `mock` | nic | atrapa z `llm/mock_server.py`, deterministyczna, zestrojona z fixture'em |
Wybór ręczny: `LLM_BACKEND=ollama task llm:up`. Konfiguracja: `llm/models.yml`.
### Zdalny endpoint (np. vLLM na OpenShifcie)
```bash
export CODEMOD_MODEL_PROVIDER=vllm
export CODEMOD_LLM_BASE_URL=https://vllm.apps.ocp.internal/v1
export CODEMOD_LLM_API_KEY=... # w CI: zmienna masked
export CODEMOD_MODEL_CODER=Qwen/Qwen3-Coder-30B
```
## Przebieg z modelem
```bash
eval "$(task llm:env)" # przy backendzie lokalnym
task run -- \
--repo /sciezka/do/repo \
--package acme-sdk --module acme --to-version 2.1.0
```
Najpierw `task plan -- --repo ... --package ... --to-version ...`. Plan czyta się szybciej niż diff.
## Kody wyjścia
| Kod | Status | Znaczenie |
|---|---|---|
| 0 | `success` / `no_changes` | zmiana gotowa albo nie było czego zmieniać |
| 1 | `failed` | weryfikacja czerwona lub błąd przebiegu |
| 3 | `blocked` | recenzent zgłosił problem blokujący - wymagana decyzja człowieka |
## Wyzwalanie w GitLabie
1. **Formularz** - Build > Pipelines > Run pipeline, pola `CODEMOD_PACKAGE`, `CODEMOD_TO_VERSION`.
2. **Issue z labelką** - webhook na zdarzenie issue → trigger token → pipeline ze zmiennymi z tytułu issue.
3. **Harmonogram** - audyt zależności (`--prompt dependency-audit --plan-only`) raz w tygodniu.
4. **Include w repozytorium docelowym** - patrz `.gitlab/ci/agentic-codemod.template.yml`.
Job `codemod:apply` jest **manualny**. To jest bramka, nie niedopatrzenie.
## Typowe sytuacje
**`EcosystemNotDetected`** - repozytorium nie ma pliku markerowego (`pyproject.toml`, `pom.xml`,
`package.json`). Dopisz adapter w `adapters/` albo wskaż komendę weryfikacji przez `--verify-command`.
**`Brak skilli w kontekście APM`** - nie wykonano `apm install` albo pakiet nie jest zadeklarowany
w `apm.yml`. Job `codemod:context` jest bramką właśnie na taką sytuację.
**Status `blocked`** - przeczytaj `review.json`. Findingi `severity: blocker` opisują konkretny problem;
`requires_human` w `plan.json` mówi, czego agent świadomie nie ruszył.
**Agent nie wywołuje żadnych narzędzi, tylko opisuje co by zrobił** - serwer nie ma włączonego
tool-callingu. vLLM wymaga `--enable-auto-tool-choice --tool-call-parser hermes` (jest w `llm/models.yml`);
przy zdalnym endpointcie sprawdź to po stronie wdrożenia.
**`task llm:up` kończy się timeoutem** - pobieranie modelu trwa dłużej niż `LLM_WAIT_TIMEOUT`.
Zajrzyj do `.runs/llm/*.log` albo `task llm:logs`; zwiększ limit: `LLM_WAIT_TIMEOUT=1800 task llm:up`.
**macOS: `vllm-metal` nie chce się zainstalować** - wymaga natywnego arm64 Pythona 3.12
(Rosetta nie wystarczy) i Xcode Command Line Tools (`xcode-select --install`).
Wariant awaryjny: `LLM_BACKEND=ollama task llm:up`.
**Weryfikacja czerwona po limicie iteracji** - `changes.patch` zawiera stan po ostatniej próbie.
Zwykle oznacza, że migracja wykracza poza wiedzę z pakietu APM. Uzupełnij notatkę migracyjną
i regułę codemod, zamiast podnosić limit iteracji.
**Nie ma deklaracji pakietu do podbicia** - `profile.json` w polu `gaps`. Sprawdź, czy wersja nie jest
pinowana w pliku lock albo w obrazie bazowym.
## Dostrajanie budżetów
| Zmienna | Domyślnie | Kiedy zmieniać |
|---|---|---|
| `CODEMOD_MAX_ITERATIONS` | 4 | rzadko - wysoki limit maskuje braki w kontekście |
| `CODEMOD_MAX_FILES_CHANGED` | 40 | monorepo z rozlanymi użyciami |
| `CODEMOD_TOOL_CALL_LIMIT` | 60 | duże repozytoria wymagające dużo rozpoznania |
| `CODEMOD_VERIFY_TIMEOUT_S` | 900 | wolne buildy (Maven, testy integracyjne) |
## Zmiana modelu
Modele per rola są w `llm/models.yml`, sekcja `models` danego backendu. Kolejność
inwestowania w większy model: `coder` (stabilne tool-calling), potem `planner`
(rozumowanie o zakresie), na końcu `reviewer`. `scribe` może zostać najmniejszy.
Po zmianie: `eval "$(task llm:env)"` w bieżącej powłoce.
## Dodanie nowego ekosystemu
1. Nowa klasa w `adapters/` dziedzicząca po `EcosystemAdapter` (5 metod).
2. Rejestracja w `adapters/__init__.py`.
3. Test w `test_adapters.py` na odczyt i podbicie wersji (`task test`).
4. Opcjonalnie: instrukcja APM z konwencjami języka (`.apm/instructions/`).
Warstwa agentowa nie wymaga zmian.
## Dodanie migracji nowego SDK
1. Notatka migracyjna dla człowieka i modelu: `references/<pakiet>-<wersja>-migration.md`.
2. Reguły maszynowe: `references/<pakiet>-<wersja>-migration.codemod.yaml`.
3. Publikacja jako pakiet APM z tagiem, dopisanie do `apm.yml` repozytoriów docelowych.
Nic w Pythonie się nie zmienia.