254 lines
18 KiB
Markdown
254 lines
18 KiB
Markdown
# Betrieb: KI-Review für git.javagil.de (Versuch mit `mi/werkprobe`)
|
||
|
||
Stand: 2026-10-05.
|
||
Diese Datei beschreibt die laufende Testintegration.
|
||
Sie enthält keine Secrets, nur deren Fundorte.
|
||
|
||
## Überblick
|
||
|
||
Werkator startet für jeden neuen Head-Commit eines offenen PRs ohne WIP-Präfix einen Review-Build.
|
||
Der Build läuft in einer Werkdock-Sandbox mit werkrevisor.
|
||
werkrevisor liest den PR über die Gitea-API, wendet feste Regeln an, lässt PR-Agent das Modell fragen und bildet das Urteil.
|
||
Danach schreibt werkrevisor einen Kommentar und ein Review in Gitea.
|
||
Werkator meldet den Exit-Code als Status `ai-review/gate` an die Head-SHA.
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
dev["Entwickler"] -- "push, PR ohne WIP" --> gitea
|
||
human["Mensch, z. B. mi"] -- "Approval bei YELLOW oder ORANGE" --> gitea
|
||
subgraph gitea["Gitea"]
|
||
pr["Pull Request"]
|
||
bp{"Branch Protection"}
|
||
end
|
||
subgraph host["mih09"]
|
||
werkator["Werkator"]
|
||
subgraph sandbox["Werkdock-Sandbox, frisches Home"]
|
||
werkrevisor["werkrevisor"] --> pragent["PR-Agent"]
|
||
end
|
||
mcfg[("Maschinen-Konfiguration:<br/>Build ai-review, Secrets,<br/>WERKREVISOR_CONFIG_TOML")]
|
||
end
|
||
openrouter["OpenRouter"] --> llm["Sprachmodell"]
|
||
|
||
werkator -- "pollt neue Heads und offene PRs" --> pr
|
||
mcfg -. "Befehl, Umgebung" .-> werkator
|
||
werkator -- "CI-Build" --> ci["normaler Build"]
|
||
werkator -- "PR-Target-Build" --> werkrevisor
|
||
werkrevisor -- "liest PR, Diff, Dateien" --> pr
|
||
pragent -- "Prompt" --> openrouter
|
||
werkrevisor -- "Kommentar, Approval / Request changes /<br/>Review-Anfrage, ai-review/assessment" --> pr
|
||
werkator -- "Status werkator und ai-review/gate" --> pr
|
||
pr --> bp
|
||
bp -- "Merge erlaubt oder gesperrt" --> dev
|
||
```
|
||
|
||
| Urteil | Gitea-Aktion von werkrevisor | `ai-review/gate` | Merge |
|
||
| ------ | ----------------------------------------------------------------- | ---------------- | ---------------------------------------- |
|
||
| GREEN | Kommentar, Review `APPROVED` für die Head-SHA | success | möglich, wenn die CI grün ist |
|
||
| YELLOW | Kommentar, offizielle Review-Anfrage an `mi` | success | gesperrt bis zu einem Approval von `mi` |
|
||
| ORANGE | Kommentar mit den Befunden, offizielle Review-Anfrage an `mi`, kein Bot-Review | success | gesperrt bis zu einem Approval von `mi` nach Prüfung der Befunde |
|
||
| RED | Kommentar, Review `REQUEST_CHANGES` mit höchstens 3 verletzten Regeln | failure | gesperrt bis zu einem neuen Commit, der die Regel erfüllt |
|
||
| ERROR | Kommentar „review not completed“, kein Review | failure | gesperrt |
|
||
|
||
Ein PR mit WIP-Präfix bekommt keinen Review und deshalb keinen `ai-review/gate`-Status; er ist damit gesperrt.
|
||
|
||
Ab werkrevisor 0.6.0 gibt es vier Farben.
|
||
RED (hart) kommt nur noch aus festen Regeln, z. B. aus offenen Fragen im PR-Dokument; kein Approval hebt es auf.
|
||
Befunde des Modells und des Classifiers Jev ergeben höchstens ORANGE (weich), weil beide irren können.
|
||
ORANGE setzt `ai-review/assessment` auf `warning` und wartet auf das Approval eines Entwicklers, der die Befunde geprüft hat.
|
||
Bis 0.5.0 ergaben Befunde des Modells RED.
|
||
|
||
Ab werkrevisor 0.7.0 gibt es zwei getrennte Größengrenzen:
|
||
|
||
- `[size] max_changed_lines` und `max_files` zählen nur Dateien außerhalb der Doku.
|
||
Ein größerer PR wird ORANGE, weil ein Mensch ihn bewusst freigeben muss; das Modell prüft ihn trotzdem.
|
||
- Die Eingabegrenze des Modells ist die kleinere aus `[model] max_context_tokens` und der Grenze, die `[budget] max_usd_per_review` bezahlt.
|
||
Ein PR darüber bekommt keinen KI-Review und wird YELLOW.
|
||
|
||
Jeder Grund aus einer Grenze nennt den Schlüssel und den Wert der Konfiguration, z. B. `[size] max_changed_lines = 800`.
|
||
|
||
Ab werkrevisor 0.8.0 kann ein Repository eine eigene `.werkrevisor.toml` haben.
|
||
werkrevisor liest sie aus dem Basis-Commit und legt sie über die Host-Konfiguration; Modell, Budget und Gate bleiben beim Host.
|
||
Für `mi/werkrevisor` enthält `config/werkrevisor.toml` nur noch den Host-Teil, den Rest `.werkrevisor.toml`.
|
||
Eine neue Version deshalb erst nach dem Merge dieser Datei installieren.
|
||
|
||
Ab werkrevisor 0.9.0 prüft der Abschnitt `[writing]` die zählbaren Schreibregeln geänderter Doku; ein Verstoß ergibt ORANGE.
|
||
|
||
Ab werkrevisor 0.10.0 sind Hauptcode mit nur hinzugefügten Tests freigebbar und DB-Schema-Pfade immer YELLOW.
|
||
|
||
Ab werkrevisor 0.11.0 zählt werkrevisor den Prompt vor dem Modellaufruf: `o200k_base` × `[model] token_count_factor` (1,15).
|
||
|
||
Ab werkrevisor 0.12.0 zählt eine Jev-Antwort unter `[classifier] min_confidence` nicht mehr für das Ergebnis.
|
||
|
||
Ab werkrevisor 0.13.0 braucht die Host-Konfiguration `[mechanical]`; mechanisch geänderte Dateien gehen nicht an das LLM.
|
||
Ab werkrevisor 0.13.1 stehen die Gründe im Kommentar nach Schwere sortiert, die schwersten zuerst.
|
||
Ab werkrevisor 0.13.2 fehlen im Build-Protokoll die erwarteten 404-Meldungen für neue und gelöschte Dateien.
|
||
Ab werkrevisor 0.14.0 hat jeder Befund des Modells eine Schwere;
|
||
`[review] orange_min_medium` ersetzt `red_min_findings`, der alte Schlüssel ist ein Konfigurationsfehler.
|
||
Ab werkrevisor 0.15.0 braucht die Konfiguration `[hard_orange]`, auch leer; eine Änderung auf diesen Pfaden ergibt ORANGE.
|
||
|
||
## Versionen
|
||
|
||
| Komponente | Version |
|
||
| ----------- | ---------------------------------------------------------------------------------------- |
|
||
| Gitea | 1.27.3 (git.javagil.de) |
|
||
| Werkator | 1.36.1 auf mih09 (`trigger.onPullRequestTarget`, PR#117; frisches Home je Review, PR#118) |
|
||
| werkrevisor | 0.4.0 (`mi/werkrevisor`), Rootfs `werkrevisor-rootfs-0.4.0.tar.zst`, Konfiguration in `WERKREVISOR_CONFIG_TOML` |
|
||
| PR-Agent | 0.47.0, Image `pragent/pr-agent@sha256:7d98954c29289846a08faf00aaea0f1f1375b1676cd8a2ed303e0af07010af7a` |
|
||
| Modell | `openrouter/anthropic/claude-sonnet-5.5` |
|
||
|
||
## Konfiguration ohne Secrets
|
||
|
||
Die Betreiber-Konfiguration ist `config/werkprobe.toml` im werkrevisor-Image (`/werkrevisor/config/werkprobe.toml`).
|
||
Ein PR kann sie nicht ändern.
|
||
Wichtige Werte:
|
||
|
||
- Preise 2,00 / 10,00 USD pro 1 Mio. Input- / Output-Tokens;
|
||
- höchstens 25.000 Input- und 4.000 Output-Tokens;
|
||
- höchstens 0,20 USD pro Review, einschließlich einer Wiederholung;
|
||
- automatisch freigebbar: nur Doku, nur hinzugefügte Tests, Doku plus Tests, Refactoring mit unveränderten Tests,
|
||
ab 0.10.0 auch Hauptcode mit nur hinzugefügten Tests;
|
||
- immer mindestens YELLOW: Pfade zu Auth, Rechten, Migrationen, Secrets, CI und Deployment (`.werkator*`, `.gitea/**`, `Dockerfile*`, Build-Dateien);
|
||
Dateien der Kategorie `docs` zählen ab 0.6.0 nie dazu, auch wenn ihr Name z. B. `rbac` enthält;
|
||
- Secret-Scan der hinzugefügten Zeilen: Ein Treffer führt zu YELLOW, ohne Modellaufruf;
|
||
unter `src/test/**` zählen ab 0.6.0 nur echte Schlüsselformate, weil Testdaten erfundene Passwörter enthalten;
|
||
- offene Fragen in einem geänderten PR-Dokument (`[pr_doc]`, ab 0.6.0): RED ohne Modellaufruf.
|
||
|
||
Ab werkrevisor 0.4.0 kann die Konfiguration stattdessen in der Maschinen-Konfiguration stehen; sie hat dann Vorrang vor der Datei im Image:
|
||
|
||
```yaml
|
||
buildCommand: python -s -m werkrevisor
|
||
werkdock:
|
||
enabled: true
|
||
rootfs: /home/storage/mih09/users/werkator/werkrevisor-rootfs-0.4.0.tar.zst
|
||
env:
|
||
PATH: /app/.venv/bin:/usr/local/bin:/usr/bin:/bin
|
||
PYTHONPATH: /werkrevisor:/app
|
||
PYTHONNOUSERSITE: "1"
|
||
WERKREVISOR_CONFIG_TOML: |
|
||
# vollständiger Inhalt von config/werkprobe.toml, angepasst
|
||
```
|
||
|
||
Modell und Reasoning-Stufe wechselt man dort über `id`, die beiden Preise und `reasoning_effort`; der nächste Review nutzt sie ohne neues Archiv.
|
||
Immer die ganze Konfiguration einfügen: Fehlende Abschnitte bekommen Standardwerte, z. B. ohne riskante Pfade.
|
||
|
||
werkdock bindet auf mih09 das gemeinsame Toolchain-Home des Repositories als `/root`; normale PR-Builds können es beschreiben.
|
||
Deshalb startet werkrevisor ab 0.4.0 nur mit `python -s` und `PYTHONNOUSERSITE=1`, liest nichts aus dem Home-Verzeichnis und bricht sonst mit Exit 30 ab.
|
||
|
||
Ab werkrevisor 0.5.0 wählt `[engine] mode` den Review:
|
||
`llm` ist der bisherige LLM-Review und der Standard, `classifier` bewertet nur die PR-Dokumente mit Jev, `combined` beides.
|
||
Für `classifier` und `combined` braucht `werkdock.env` zusätzlich `TYPESAFE_API_KEY`;
|
||
`config/werkprobe.toml` enthält dafür schon den Abschnitt `[classifier]` mit Fragen und Schwellwerten.
|
||
|
||
Die Werkator-Build-Definition steht in der Maschinen-Konfiguration auf mih09:
|
||
`~/werkprobe/.git/werkator/.werkator.yml` (Rechte `600`), Build `ai-review`.
|
||
Ein Branch kann diese Definition weder ändern noch abschalten.
|
||
|
||
## Secrets und Rechte
|
||
|
||
| Secret | Fundort | Rechte |
|
||
| -------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||
| `GITEA_BOT_TOKEN` | mih09: `werkdock.env` in `~/werkprobe/.git/werkator/.werkator.yml` | Token `pr-agent-bot` von `ai-reviewer`: repository, issue (Lesen und Schreiben), user (Lesen) |
|
||
| `OPENROUTER_API_KEY` | ebenda | OpenRouter-Key `review-agents`, Limit 5 USD, monatlich zurückgesetzt |
|
||
| `TYPESAFE_API_KEY` | noch nicht eingerichtet; gehört neben `OPENROUTER_API_KEY` | TypeSafe-Key für Jev, nur für `[engine] mode` `classifier` oder `combined` |
|
||
| Werkator-Token | mih09: `~/.werkator.yml`, `defaults.git.token` | Token von `mi`; meldet die Werkator-Status |
|
||
|
||
`ai-reviewer` ist Mitarbeiter mit Schreibrecht nur in `mi/werkprobe`; ohne Schreibrecht zählt sein Approval nicht als offiziell.
|
||
Das Konto darf keine Repositories oder Organisationen anlegen.
|
||
Eine lokale Kopie der Secrets für Tests liegt auf dem Arbeitsrechner in `~/.config/pr-agent-test/secrets.env` (Rechte `600`).
|
||
|
||
### OpenRouter-Key austauschen
|
||
|
||
1. Auf https://openrouter.ai/settings/keys einen neuen Key mit Limit anlegen.
|
||
2. Auf mih09 in `~/werkprobe/.git/werkator/.werkator.yml` den Wert von `OPENROUTER_API_KEY` ersetzen.
|
||
Werkator liest die Datei beim nächsten Poll, ein Neustart ist nicht nötig.
|
||
3. Den alten Key auf OpenRouter löschen.
|
||
|
||
## Branch Protection von `main` in `mi/werkprobe`
|
||
|
||
| Feld | Wert | Zweck |
|
||
| ------------------------------------ | ---------------------------- | -------------------------------------------------------- |
|
||
| `enable_status_check` | `true` | |
|
||
| `status_check_contexts` | `werkator`, `ai-review/gate` | CI und KI-Review sind Pflicht |
|
||
| `required_approvals` | `1` | das Approval ist die Sperre für YELLOW |
|
||
| `enable_approvals_whitelist` | `true` | |
|
||
| `approvals_whitelist_username` | `ai-reviewer`, `mi` | GREEN-Bot-Approval oder menschliches Approval |
|
||
| `block_on_rejected_reviews` | `true` | RED sperrt |
|
||
| `block_on_official_review_requests` | `true` | die YELLOW- oder ORANGE-Anfrage an `mi` sperrt |
|
||
| `dismiss_stale_approvals` | `true` | neue Commits entwerten alte Approvals |
|
||
| `block_on_outdated_branch` | `true` | eine bewegte Basis braucht ein Update und damit einen neuen Review |
|
||
|
||
Die bisherige Regel ist gesichert in `~/.config/pr-agent-test/werkprobe-branch-protections.backup-2026-10-04.json`.
|
||
Sie verlangte nur den Status `werkator` und kein Approval.
|
||
|
||
## Abnahme
|
||
|
||
| Fall | PR | Ergebnis |
|
||
| ------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||
| 1. harmlose Änderung | [#8](https://git.javagil.de/mi/werkprobe/pulls/8) | GREEN, Bot-Approval, gemergt ohne menschliches Approval |
|
||
| 2. riskanter Bereich | [#9](https://git.javagil.de/mi/werkprobe/pulls/9) | YELLOW, Merge abgelehnt („Does not have enough approvals“), nach Approval von `mi` gemergt |
|
||
| 3. eingebauter Fehler | [#10](https://git.javagil.de/mi/werkprobe/pulls/10) | RED mit 2 zutreffenden Befunden, Merge abgelehnt; ab 0.6.0 wäre das ORANGE |
|
||
| 4. neuer Head, bewegte Basis | #8 | Merge mit alter Basis abgelehnt; nach dem Update gesperrt bis zum neuen GREEN |
|
||
| 5. ungültiger Key, Timeout, Budget, abgeschnittene Antwort | lokal mit `tools/dry-run` | jeweils ERROR, nie GREEN |
|
||
| 6. wiederholter Lauf | #10, Neustart in Werkator | Urteil wiederverwendet, kein Modellaufruf, kein zweiter Kommentar |
|
||
|
||
Modellqualität und Gate-Mechanik sind getrennt zu bewerten:
|
||
|
||
- `gemini-3.8-flash` hat den Fehler in #10 übersehen, Sonnet 5.5 hat ihn gefunden.
|
||
- Einen Fehler, der eine Randbedingung außerhalb des Diffs verletzt ([#11](https://git.javagil.de/mi/werkprobe/pulls/11)), fanden weder Sonnet 5.5 noch DeepSeek V4 Pro noch GPT-6 Astra „high“.
|
||
Den Merge hat dort die feste Regel verhindert (Kategorie `mixed`).
|
||
|
||
## Kosten
|
||
|
||
Gemessen mit den von OpenRouter gemeldeten Kosten:
|
||
|
||
| Lauf | Tokens (Input / Output) | Kosten |
|
||
| ----------------------------- | ----------------------- | ---------- |
|
||
| #8, Doku-Änderung, GREEN | 2.936 / 94 | 0,0068 USD |
|
||
| #9, CI-Konfiguration, YELLOW | 3.030 / 315 | 0,0092 USD |
|
||
| #10, Fehler, RED | 3.497 / 590 | 0,0129 USD |
|
||
|
||
Der ganze Versuch mit allen lokalen Vergleichsläufen kostete 0,142 USD.
|
||
Jeder Kommentar nennt die Kosten seines Laufs; Werkator archiviert `build/werkrevisor/usage.jsonl` als Artefakt.
|
||
Fehlende Verbrauchsdaten stehen dort als `unknown`, geschätzte Kosten als `estimated`.
|
||
Ab 0.7.0 steht ein Lauf ohne Modellaufruf als `none` mit 0 USD; der Kommentar sagt dann „no model call, cost 0 USD“.
|
||
|
||
## Bekannte Grenzen
|
||
|
||
- Das Modell sieht den Diff und die vollständigen geänderten Dateien (ab 0.3.0, `full_files = true`), aber keine anderen Dateien.
|
||
Randbedingungen, die nur in nicht geänderten Dateien stehen, erkennt es nicht.
|
||
Mit Diff allein hat kein getestetes Modell den Fehler in #11 gefunden; mit den vollständigen Dateien fand Sonnet 5.5 ihn.
|
||
- Ein menschliches Approval startet keinen neuen Review; `ai-review/assessment` zeigt deshalb weiter den Stand des letzten Reviews.
|
||
Für den Merge ist nur `ai-review/gate` verpflichtend.
|
||
- Wenn nur die Basis sich bewegt, startet kein neuer Review; `block_on_outdated_branch` erzwingt dann ein Update.
|
||
- Review-Builds teilen sich das Limit `maxConcurrentPerRepository: 1` mit der CI und warten auf sie.
|
||
- Bei Modellen mit wechselnden Anbietern (z. B. DeepSeek) kann der tatsächliche Preis über dem eingetragenen liegen.
|
||
- `block_admin_merge_override` ist `false`: Ein Gitea-Administrator kann die Sperre beim Merge übergehen.
|
||
|
||
## Stoppen, neu starten, zurückbauen
|
||
|
||
**Reviews anhalten:** In `~/werkprobe/.git/werkator/.werkator.yml` den Block `builds.ai-review` auskommentieren.
|
||
Solange `ai-review/gate` Pflicht ist, sind dann alle PRs gesperrt; deshalb gleichzeitig die Branch Protection zurücksetzen (siehe unten) oder nur `ai-review/gate` aus `status_check_contexts` nehmen.
|
||
|
||
**Neu starten:** Den Block wieder aktivieren.
|
||
Einen einzelnen Review wiederholt die Werkator-Oberfläche („Restart“) am Build `<branch>@ai-review`.
|
||
|
||
**Neue werkrevisor-Version:** Mit `tools/build-rootfs` ein Archiv bauen, nach `/home/storage/mih09/users/werkator/` kopieren und in der Maschinen-Konfiguration den Pfad `werkdock.rootfs` auf das neue Archiv setzen.
|
||
Beim Wechsel auf 0.4.0 zusätzlich `buildCommand: python -s -m werkrevisor` und `PYTHONNOUSERSITE: "1"` in `werkdock.env` setzen.
|
||
Jede Version braucht einen eigenen Dateinamen.
|
||
|
||
**Vollständiger Rückbau:**
|
||
|
||
1. Branch Protection wiederherstellen, mit einem Admin-Token von `mi`:
|
||
|
||
```bash
|
||
curl -X PATCH -H "Authorization: token $GITEA_SETUP_TOKEN" -H 'Content-Type: application/json' \
|
||
https://git.javagil.de/api/v1/repos/mi/werkprobe/branch_protections/main \
|
||
-d '{"status_check_contexts":["werkator"],"required_approvals":0,"enable_approvals_whitelist":false,"approvals_whitelist_username":[],"block_on_rejected_reviews":false,"block_on_official_review_requests":false,"dismiss_stale_approvals":false,"block_on_outdated_branch":false}'
|
||
```
|
||
|
||
2. Auf mih09 die Maschinen-Konfiguration aus `~/werkprobe/.git/werkator/.werkator.yml.bak-2026-10-05-before-ai-review` zurückkopieren.
|
||
3. Die Archive `werkrevisor-rootfs-*.tar.zst` in `/home/storage/mih09/users/werkator/` löschen; Werkator entfernt das zugehörige Werkdock-Image selbst.
|
||
4. In Gitea `ai-reviewer` und `werkprobe-dev` aus den Mitarbeitern von `mi/werkprobe` entfernen, ihre Tokens löschen und die Konten deaktivieren.
|
||
5. Den OpenRouter-Key `review-agents` löschen, sofern ihn nichts anderes mehr nutzt.
|
||
6. Den Einrichtungs-Token `pr-agent-setup` von `mi` löschen.
|