This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/docs/intent/board-reviewer-visual-verification.md
pijukka 9cb6111fed board: give the Claude Reviewer real visual verification for HTML/CSS diffs
A live review report (web-ui-system-darkmode, commit 4b2047e) flagged
two uncertainties a text-only diff review genuinely can't close: no
actual rendering checked, and contrast only "visually plausibilized"
from hex values. Adds a fixed, narrowly-scoped screenshot.py: given a
commit hash and a repo-relative .html path, it extracts that exact file
via `git show <hash>:<path>` (never the live working tree, which may
have moved on by review time) and renders it headless in both light and
dark prefers-color-scheme, for the Reviewer to inspect via its own Read
tool.

Wired only into run_review()'s Claude branch -- Codex's --sandbox
read-only blocks every write, so Chromium has nowhere to put a
screenshot there. --allowedTools gets a second, dynamically-built entry
pinning the exact resolved script invocation, same fixed-command
precedent as the existing Bash(git *) grant; the script re-validates
its own arguments regardless.

Two things verified live before wiring anything up, not assumed:
--force-prefers-color-scheme has no effect in this Chromium build
(--blink-settings=preferredColorScheme=<0|1> does, confirmed by actual
pixel color); and --allowedTools does honor a fixed-script pattern the
same way it honors Bash(git *). New pkgs.chromium dependency, run with
--no-sandbox since NoNewPrivileges=true already forbids the privilege
escalation Chromium's own setuid sandbox needs -- containment comes
from the outer systemd sandbox instead, a deliberate trade-off noted in
both the intent doc and README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016w25covboHfcYuLXBAvX3g
2026-09-10 19:01:05 +02:00

5.5 KiB

Intent: Visuelle Browser-Verifikation für den Board-Reviewer

Ziel

Der Reviewer-Bericht für web-ui-system-darkmode (Commit 4b2047e) listete zwei "Unsicherheiten", die ein reines Text-Diff-Review nicht schließen kann: kein tatsächliches Rendering geprüft (nur die CSS-Variablenstruktur), keine berechnete WCAG-Kontrastprüfung, nur "visuell plausibilisiert anhand der gewählten Hex-Werte". Live-Diskussion auf dem Board selbst (10.09.2026): dem Reviewer eine echte visuelle Prüfmethode für HTML/CSS-Änderungen geben, statt nur aus dem Quelltext auf das Rendering zu schließen.

Umfang

  • Neues, fixes Skript board-agent/screenshot.py: nimmt ausschließlich einen Commit-Hash und einen Repo-relativen .html-Pfad entgegen, extrahiert den exakten Dateiinhalt zum genannten Commit über git show <hash>:<pfad> (nie den möglicherweise inzwischen weitergelaufenen Arbeitsbaum), rendert ihn headless per Chromium je einmal mit prefers-color-scheme: light und : dark und gibt beide PNG-Pfade auf stdout aus.
  • Empirisch verifiziert (10.09.2026), nicht angenommen: --force-prefers-color-scheme hat in der eingesetzten Chromium-Version headless keinerlei Effekt (identischer Output für beide Werte); erst --blink-settings=preferredColorScheme=<0|1> schaltet tatsächlich um (0 = dark, 1 = light, per gemessenem Pixelwert bestätigt, nicht aus der Enum-Reihenfolge angenommen).
  • run_review()s Claude-Zweig bekommt einen neuen, dynamisch aus BOARD_SCREENSHOT_SCRIPT/sys.executable zusammengesetzten --allowedTools-Eintrag, der ausschließlich genau diesen einen Skriptaufruf erlaubt (gleiches "Wrapper erzwingt, Modell schlägt vor"- Muster wie der bestehende Bash(git *)-Eintrag) — das Skript validiert seine beiden Argumente zusätzlich selbst (Commit-Hash-Form, .html-Endung, kein absoluter Pfad, kein ..), unabhängig vom Allowlist-Muster.
  • REVIEW_PROMPT_TEMPLATE bekommt einen neuen, laufzeitabhängigen {visual_verification}-Abschnitt: für Claude die vollständige Gebrauchsanweisung inklusive des vorausgefüllten Befehls (Commit-Hash bereits eingesetzt, der Reviewer ergänzt nur noch den Dateipfad); für Codex/OpenRouter bleibt er leer.
  • Nur für die Claude-Runtime verdrahtet: Codex' run_review()-Zweig läuft mit --sandbox read-only, das jeden Schreibzugriff verweigert (verifiziert, siehe board-agent/README.md, "Why read-only, not a scoped writable dir") — Chromium hätte dort keinen Ort, um einen Screenshot abzulegen, also wird das Werkzeug dort erst gar nicht angeboten.
  • Neue Nix-Abhängigkeit pkgs.chromium (BOARD_CHROMIUM_BIN). Läuft mit --no-sandbox, da Chromiums eigener Setuid-Sandbox die Rechteausweitung braucht, die NoNewPrivileges = true auf board-agent-worker.service bereits verbietet — Eindämmung kommt in diesem Modus vollständig vom äußeren systemd-Sandbox (ProtectSystem=strict, PrivateTmp), nicht von Chromiums eigener Prozessisolation. Bewusster Kompromiss, kein Versehen: der gerenderte Inhalt ist in der Praxis ausschließlich dieses Repos eigene, bereits geprüfte Commits, nie Drittinhalte.
  • Ein fehlgeschlagener Screenshot-Aufruf (z. B. weil die Sandbox Chromium doch nicht starten lässt) ist laut Prompt kein Fehler des Reviews selbst — der Reviewer vermerkt das knapp unter "Unsicherheiten" und bewertet den Rest des Diffs normal weiter.

Explizit ausgeschlossen

  • Keine automatische, berechnete WCAG-Kontrastprüfung (Diskussion ausdrücklich zugunsten des Browser-Screenshots vertagt) — der Reviewer beurteilt Kontrast weiterhin visuell aus dem Screenshot, nicht rechnerisch exakt. Eigener, separater Folgeintent, falls gewünscht.
  • Kein Einsatz für Codex/OpenRouter-Reviews (siehe oben, Sandbox-Grund).
  • Keine Ausweitung auf beliebige URLs oder externe Seiten — das Skript akzeptiert ausschließlich einen Repo-relativen .html-Pfad und lehnt jeden anderen selbst ab.
  • Keine Chromium-eigene Sandbox (--no-sandbox ist zwingend unter den bestehenden systemd-Härtungsoptionen) — kein Versuch, das zu umgehen, da es dafür eine echte Kompromittierung der bestehenden NoNewPrivileges-Einstellung bräuchte.

Annahmen

  • Board-getriggerte Reviews betreffen aktuell ausschließlich board.html als einzige eigenständige HTML/CSS-Oberfläche dieses Repos (per Operator-Analyse zum Darkmode-Auftrag bereits bestätigt) — das Skript ist aber nicht auf diesen einen Dateinamen fest verdrahtet, falls eine zweite Oberfläche dazukommt.
  • Ein einzelnes, aus dem Commit extrahiertes HTML-Dokument ist selbstständig renderbar (kein externer Asset-Import über relative Pfade) — trifft auf board.html zu (vollständig inline CSS/JS), ist aber keine allgemeingültige Eigenschaft jeder künftigen HTML-Datei in diesem Repo.

Offene Risiken

  • Noch nicht gegen den echten board-agent-worker.service-Sandbox live verifiziert (nur isoliert außerhalb des systemd-Sandbox getestet, 10.09.2026) — falls eine dort zusätzlich aktive Härtungsoption (SystemCallFilter o. Ä.) Chromium doch blockiert, greift der im Prompt vorgesehene "kein Fehler, nur Unsicherheiten-Vermerk"-Fallback, aber das ist noch nicht am echten Dienst beobachtet worden.
  • pkgs.chromium ist eine vergleichsweise schwere neue Abhängigkeit für ein sonst bewusst kleines, forkbares Grundsystem (siehe docs/operator-prompt.md §0) — explizit in Kauf genommen für diesen konkreten Anwendungsfall, kein Freibrief für weitere Browser-Automatisierung ohne erneute Abwägung.