See docs/prs for the PR document. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #121 Co-authored-by: claude-code <claude-code@javagil.de>
158 lines
8.9 KiB
Markdown
158 lines
8.9 KiB
Markdown
# Konzept: Diagramme von PR Lens im Review-Kommentar
|
||
|
||
Stand: 2026-10-18, Übergabe aus einer Sitzung im Repo hsadmin-ng; noch nichts umgesetzt.
|
||
|
||
## Ziel
|
||
|
||
werkrevisor macht das Review weiter allein.
|
||
Zusätzlich hängt es an seinen Review-Kommentar ein Architektur- und Datenfluss-Diagramm des PR.
|
||
Das Diagramm hilft dem menschlichen Reviewer zu sehen, was ein PR berührt.
|
||
Die Risiko-Triage von werkrevisor verbessert es nicht direkt.
|
||
|
||
## PR Lens
|
||
|
||
[PR Lens](https://prlens.dev) von Coldtea erzeugt animierte Diagramme zu einem PR;
|
||
der Kern ist MIT-lizenziert: [github.com/coldteadotai/pr-lens](https://github.com/coldteadotai/pr-lens).
|
||
|
||
- Die GitHub-App macht Diagramm und Review, unterstützt aber nur GitHub, GitLab und Bitbucket, kein Gitea.
|
||
- Das CLI-Kommando `analyze` liest den Diff gegen die Merge-Base, fragt ein LLM und schreibt `.pr-lens/graph.json`.
|
||
Anbieter: `gemini` (Standard), `openai` oder `openai-compatible` mit eigener Base-URL.
|
||
Nur `analyze` ruft ein Modell auf.
|
||
- Das Format von `graph.json` beschreibt `packages/schema`.
|
||
- `@coldtea/pr-lens-renderer` (npm, ohne Abhängigkeiten) macht aus `graph.json` animierte SVGs, hell und dunkel,
|
||
und ein Manifest.
|
||
- Für GitLab lädt PR Lens die SVGs als Anhänge ins Projekt hoch, für Bitbucket in die Downloads;
|
||
für Gitea gibt es nichts Fertiges.
|
||
|
||
Das Review von PR Lens brauchen wir nicht, es deckt sich mit werkrevisor.
|
||
|
||
## Was werkrevisor schon hat
|
||
|
||
- `werkrevisor/gitea.py`: `create_issue_comment`, `create_review` und `edit_issue_comment` mit `GITEA_TOKEN`.
|
||
Der Client sendet bisher nur JSON.
|
||
- OpenRouter als LLM-Zugang, für hsadmin-ng mit `openrouter/anthropic/claude-sonnet-5.5` (`config/hsadmin-ng.toml`).
|
||
OpenRouter ist OpenAI-kompatibel; `analyze` kann es also mit `openai-compatible` nutzen.
|
||
|
||
## Zwei Betriebsarten
|
||
|
||
- **git.javagil.de:** werkrevisor läuft als Build in Werkator und kommentiert selbst; hier ist der volle Ablauf unten möglich.
|
||
- **dev.hostsharing.net:** dort läuft nur `hs-review` auf der Workstation;
|
||
es schreibt einen Entwurf, nur lesend, und der Mensch stellt die Befunde selbst ein.
|
||
Die Schritte 3 und 4 entfallen; `hs-review` legt die SVGs neben den Entwurf,
|
||
und der Mensch hängt sie beim Einstellen an.
|
||
|
||
## Möglicher Ablauf
|
||
|
||
1. `graph.json` erzeugen, nach Variante A oder B.
|
||
2. `pr-lens-renderer` erzeugt die SVGs.
|
||
3. werkrevisor legt den Review-Kommentar an
|
||
und lädt die SVGs mit `POST /repos/{owner}/{repo}/issues/comments/{id}/assets` (multipart) hoch.
|
||
4. werkrevisor ergänzt den Kommentar per `edit_issue_comment` um die Bildlinks.
|
||
|
||
**Variante A:** werkrevisor ruft `pr-lens analyze` mit OpenRouter als `openai-compatible` auf.
|
||
Der Prompt von PR Lens bleibt in Gebrauch; das kostet aber einen zweiten LLM-Aufruf je Review.
|
||
|
||
**Variante B:** Das Review-LLM liefert `graph.json` im selben Lauf mit.
|
||
Das ist günstiger, und der Diff geht an kein zweites Modell.
|
||
Dafür müssen wir den Prompt für das Schema selbst schreiben und pflegen.
|
||
|
||
## Grenzen des Diagramms
|
||
|
||
Das Diagramm ist nur eine Sichthilfe, keine Aussage darüber, was der PR tut.
|
||
In beiden Varianten geht der Inhalt des PR in den Prompt.
|
||
Ein gezielt gebauter PR kann so ein harmlos wirkendes Diagramm erzeugen,
|
||
das z. B. einen Abfluss von Daten verschweigt;
|
||
die Messung vom 2026-10-17 zeigt, dass ein solcher Abfluss in einer passenden Datei leicht übersehen wird.
|
||
Der Kommentar sagt das beim Diagramm in einem Satz.
|
||
|
||
## Neue Abhängigkeiten
|
||
|
||
- Node.js für CLI und Renderer.
|
||
Das ist eine neue Technik und braucht eine eigene Entscheidung des Menschen, mit den Alternativen, z. B.:
|
||
- Node.js nur in einem Container, nicht auf dem Host;
|
||
- den Renderer nach Python portieren, er hat keine Abhängigkeiten;
|
||
- nur Variante B, dann braucht es `analyze` nicht.
|
||
- Multipart-Upload im `GiteaClient`.
|
||
- Kein neuer Token, falls der Upload mit derselben Berechtigung `write:issue` geht wie das Kommentieren.
|
||
|
||
## Lieferkette
|
||
|
||
werkrevisor läuft wie `pull_request_target` und hält `GITEA_TOKEN` und den Schlüssel für OpenRouter.
|
||
|
||
- Die npm-Pakete laufen mit festen Versionen und Prüfsummen, in einem Container ohne `GITEA_TOKEN`.
|
||
- `analyze` bekommt nur den Schlüssel für OpenRouter, der Renderer keinen Schlüssel und kein Netz.
|
||
- Mit npm werden `npm audit` und `license-checker` Pflicht-Prüfungen im Build (siehe agentic-workflow).
|
||
|
||
## Offene Fragen
|
||
|
||
- [x] TODO.svg: Lässt `[attachment] ALLOWED_TYPES` auf dev.hostsharing.net und git.javagil.de SVG-Anhänge zu?
|
||
Ja, auf beiden; aber höchstens 5 Dateien je Kommentar (`max_files`), bei 100 MB je Datei.
|
||
- [x] TODO.anzeige: Zeigt Gitea ein angehängtes SVG im Kommentar mit Animation an, oder nur als Download?
|
||
Inline und animiert; Gitea liefert `image/svg+xml` mit einer CSP, die Skripte sperrt.
|
||
- [x] TODO.zugriff: Sind Anhänge privater Repos nur für angemeldete Benutzer mit Leserecht sichtbar?
|
||
Ja; ohne Anmeldung kommt 404.
|
||
- [x] TODO.token: Reicht `write:issue` des Bot-Tokens für den Upload?
|
||
Mit dem Token von `claude-code` geht es; das Bot-Token hat keinen Zugriff auf werkrevisor-bench.
|
||
Das erste eingeschaltete Repo zeigt es; ein fehlendes Recht gibt nur eine Zeile im Log.
|
||
- [x] TODO.dateien: Höchstens 5 Anhänge je Kommentar, PR Lens macht je Diagramm 2 SVGs.
|
||
Nur die helle Fassung, höchstens `max_diagrams = 5`.
|
||
- [x] TODO.variante: Variante A oder B; dafür Kosten und Qualität an einigen hsadmin-ng-PRs messen.
|
||
Variante A, entschieden am 2026-10-18 nach dem Versuch unten.
|
||
- [x] TODO.schalter: Wie heißt der Schalter je Repo in der Konfiguration, und gilt er als Standard aus?
|
||
`[diagrams] enabled`, nur in der Host-Konfiguration, Standard aus.
|
||
- [x] TODO.grenzen: Was passiert bei großen Diffs oder einem Fehler von `analyze`?
|
||
Ein fehlendes Diagramm darf das Review nie beenden und das Ergebnis nicht ändern.
|
||
Der Diff wird nach `max_diff_bytes` abgeschnitten; ein Fehler oder eine Zeitüberschreitung gibt nur eine Zeile im Log.
|
||
|
||
## Versuch von Hand, 2026-10-18
|
||
|
||
Ohne Änderung an werkrevisor, für hsadmin-ng PR#319 (Tarife am Partner, 36 Dateien, +1489 −13):
|
||
|
||
- `@coldtea/pr-lens-cli@0.13.2` in `node:22` unter Podman; MIT, 4 Abhängigkeiten, `npm audit` ohne Befund.
|
||
- `analyze` spricht nur mit dem genannten Anbieter; `prlens.dev` nur bei `canvas` und `auth`.
|
||
- `analyze --provider openai-compatible --base-url https://openrouter.ai/api/v1 --model anthropic/claude-sonnet-5.5`,
|
||
nur mit `OPENROUTER_API_KEY` im Container: 174 KB Prompt (etwa 45k Token), 35 s,
|
||
15 Knoten, 16 Kanten, 2 Abläufe.
|
||
- `render` ohne Netz: 3 Diagramme (Architektur, 2 Abläufe), je hell und dunkel; keine Skripte, keine externen Verweise.
|
||
- Inhalt brauchbar: Controller, Tarif-Domäne, neuer Trigger und neue Tabelle, mit NEW und CHANGED markiert.
|
||
- Upload mit `POST /repos/{owner}/{repo}/issues/comments/{id}/assets` und Bildlinks per Edit:
|
||
[werkrevisor-bench Issue 89](https://git.javagil.de/mi/werkrevisor-bench/issues/89).
|
||
|
||
Variante B, nachgestellt mit einem eigenen Skript:
|
||
ein Aufruf mit kurzer Review-Anweisung, dem System-Prompt von PR Lens, Schema und Diff;
|
||
die Antwort ist `{"review": …, "graph": …}`.
|
||
|
||
- 71k Token Eingabe, 5,3k Ausgabe, 31 s, 0,195 USD.
|
||
- `graph` gültig im ersten Versuch: 11 Knoten, 14 Kanten, 2 Abläufe; die Abläufe zeigen auch die 400-Fälle.
|
||
- Das Review daneben ist brauchbar: 5 Befunde mit Datei und Zeile.
|
||
|
||
Kosten je Review mit `anthropic/claude-sonnet-5.5` (2 USD je 1M Token Eingabe, 10 USD je 1M Ausgabe):
|
||
|
||
| Variante | Zusatzkosten | Grund |
|
||
|----------|--------------|--------------------------------------------------------------|
|
||
| A | etwa 0,21 USD | Diff und Anweisungen ein zweites Mal, dazu etwa 7k Token Ausgabe |
|
||
| B | etwa 0,10 USD | nur Anweisungen und Schema (etwa 15k Token) und die Ausgabe |
|
||
|
||
Haken an B: werkrevisor fragt das Review-Modell über PR-Agent 0.47.0;
|
||
dessen Antwort ist ein festes YAML-Format, `extra_instructions` ändern es nicht.
|
||
B im selben Lauf heißt also, in PR-Agent einzugreifen, oder einen eigenen Aufruf neben PR-Agent;
|
||
der eigene Aufruf ist aber Variante A mit eigenem Prompt.
|
||
Für das Rendern braucht B weiter Node.js, außer der Renderer wird portiert.
|
||
|
||
## Entscheidung, 2026-10-18
|
||
|
||
Variante A, mit dem Diff aus der Gitea-API statt einer Arbeitskopie: `werkrevisor/pr_lens.mjs` nutzt Prompt,
|
||
Schema und Prüfung des festgelegten PR Lens.
|
||
Node.js und PR Lens liegen im schreibgeschützten Basis-Archiv, nicht in einem Container:
|
||
die Sandbox von werkdock auf mih09 hat keine Container-Laufzeit.
|
||
nvm im Konto `mih09-werkator` hilft nicht: die Sandbox sieht es nicht,
|
||
und das Toolchain-Home, das sie einbindet, können normale PR-Builds beschreiben.
|
||
Der Kindprozess bekommt nur den Schlüssel für OpenRouter;
|
||
er könnte als derselbe Benutzer aber die Umgebung von werkrevisor aus `/proc` lesen.
|
||
Je Host abschaltbar, Standard aus.
|
||
Auf git.javagil.de hängt werkrevisor die hellen SVGs an seinen Kommentar; `hs-review` folgt in einem eigenen PR.
|
||
|
||
## Nächster Schritt
|
||
|
||
Umsetzung in werkrevisor PR#121; siehe `docs/prs/2026-10-18-PR#121-pr-lens-diagrams.md` und `docs/DECISIONS.md`.
|