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.