# runner-agents.md — test-samurai Runner-API Ziel: Diese Datei beschreibt die öffentliche Runner-API, die ein neuer Runner implementieren muss, damit alle Commands vollständig unterstützt werden. ## Modulform (Pflicht) - Der Runner ist ein Lua-Modul, das eine Table mit Funktionen zurückgibt. - Beispiel: - `local runner = {}` - `return runner` - Der Modulpfad in `runner_modules` muss exakt zum Dateipfad unter `lua/` passen. - Beispiel: `lua/test-samurai-go-runner/init.lua` -> `require("test-samurai-go-runner")`. - `lua/init.lua` wäre `require("init")` und ist kollisionsanfällig; vermeiden. ## Pflichtfunktionen (volle Command- und Keymap-Unterstützung) - `is_test_file(bufnr) -> boolean` - Wird für die Runner-Auswahl genutzt. - `find_nearest(bufnr, row, col) -> spec|nil, err?` - Für `TSamNearest`. - `spec.file` muss gesetzt sein. - Bei Fehler/kein Treffer: `nil, "reason"` zurückgeben. - `build_command(spec) -> command_spec` - Für `TSamNearest`. - `build_file_command(bufnr) -> command_spec` - Für `TSamFile`. - `build_all_command(bufnr) -> command_spec` - Für `TSamAll`. - `build_failed_command(last_command, failures, scope_kind) -> command_spec` - Für `TSamFailedOnly`. - `parse_results(output) -> results` - Fallback-Parser für vollständigen Output. - `output_parser() -> { on_line, on_complete }` - Muss Streaming unterstützen (Listing wird live befüllt). - `parse_test_output(output) -> table` - Detail-Ausgabe pro Test (Detail-Float und `` im Listing). - `collect_failed_locations(failures, command, scope_kind) -> items` - Quickfix-Unterstützung (Keymap `qn`). ## Output-Parsing (Listing + Summary) Beide Varianten müssen vorhanden sein: - `parse_results(output) -> results` - `results` muss enthalten: - `passes` (Array von Namen) - `failures` (Array von Namen) - `skips` (Array von Namen) - Optional: - `display = { passes = {}, failures = {}, skips = {} }` - `failures_all` (für Streaming-Parser, um alle bisherigen Failures zu liefern) - Wenn `display` fehlt, werden `passes/failures/skips` direkt im Listing angezeigt. - oder `output_parser() -> { on_line, on_complete }` - `on_line(line, state)` kann `results` liefern (siehe oben). - `on_complete(output, state)` kann `results` liefern (siehe oben). - `on_line` muss Ergebnisse liefern, damit das Listing-Float immer gestreamt wird. Hinweis: `parse_results` darf intern `output_parser().on_complete` nutzen, aber beide Funktionen müssen existieren. ## Listing-Gruppierung fuer Parent/Subtests - Wenn Testnamen das Format `Parent/Subtest` oder verschachtelt `Parent/Sub/Subtest` haben und der Parent ebenfalls in den Ergebnissen vorhanden ist, gruppiert das Listing: - Parent kommt vor seinen direkten Kindern. - Mehrstufige Subtests werden hierarchisch gruppiert (Parent -> Kind -> Enkel). - Die Reihenfolge der Kinder folgt der Eingangsreihenfolge des Runners. - Ohne Parent-Eintrag bleibt die normale Reihenfolge erhalten. ## Detail-Output (TSamShowOutput / im Listing) - `parse_test_output(output) -> table` - Rückgabeform: - `{ [test_name] = { "line1", "line2", ... } }` - `test_name` muss mit `results.*` korrespondieren (gleiches Namensschema). ## Quickfix-Unterstützung (Failures) - `collect_failed_locations(failures, command, scope_kind) -> items` - `items`: Array von Quickfix-Items - `{ filename = "...", lnum = , col = , text = "..." }` ## Erwartete Datenformen - `command_spec`: - `{ cmd = { "binary", "arg1", ... }, cwd = "..." }` - `cmd` darf nicht leer sein. - `cwd` ist optional; wenn nicht gesetzt, nutzt der Core das aktuelle CWD. - `spec` (von `find_nearest`): - Muss mindestens `file` enthalten, z. B.: - `{ file = "...", cwd = "...", test_name = "...", full_name = "...", kind = "..." }` - `results` (für Listing-Float + Keymaps): - `{ passes = { "Name1", ... }, failures = { "Name2", ... }, skips = { "Name3", ... } }` - Optional: `display = { passes = { "DisplayName1", ... }, failures = { "DisplayName2", ... }, skips = { "DisplayName3", ... } }` - `failures` steuert `[ FAIL ]`-Zeilen im Listing und wird von `nf`/`pf` genutzt. - `items` (für Quickfix): - `{ { filename = "...", lnum = 1, col = 1, text = "..." }, ... }` - Wird von `qn` verwendet. ## Keymaps (Datenlieferung) - `nf` / `pf` - benötigt `[ FAIL ]`-Einträge im Listing. - Runner muss `results.failures` (und optional `display.failures`) liefern. - `qn` - springt in die Quickfix-Liste. - Runner muss `collect_failed_locations` implementieren und gültige `items` liefern. - `` im Listing - öffnet Detail-Float. - Runner muss `parse_test_output` liefern und Testnamen konsistent zu `results.*` halten. ## Optional empfohlene Metadaten - `name` (String) - Wird in Fehlermeldungen und Logs angezeigt. - `framework` (String) - Wird zur Framework-Auswahl (z. B. JS) genutzt. ## Prompt-Beispiel "Erstelle mir anhand der `runner-agents.md` einen neuen Runner für Rust." ## Minimaler Runner-Skeleton (Template) ```lua local runner = { name = "my-runner", framework = "my-framework", } function runner.is_test_file(bufnr) return false end function runner.find_nearest(bufnr, row, col) return nil, "no test call found" end function runner.build_command(spec) return { cmd = { "echo", "not-implemented" }, cwd = spec.cwd } end function runner.build_file_command(bufnr) return { cmd = { "echo", "not-implemented" } } end function runner.build_all_command(bufnr) return { cmd = { "echo", "not-implemented" } } end function runner.build_failed_command(last_command, failures, scope_kind) return { cmd = { "echo", "not-implemented" }, cwd = last_command and last_command.cwd or nil } end function runner.parse_results(output) return { passes = {}, failures = {}, skips = {}, display = { passes = {}, failures = {}, skips = {} } } end function runner.output_parser() return { on_line = function(_line, _state) return nil end, on_complete = function(_output, _state) return runner.parse_results(_output) end, } end function runner.parse_test_output(output) return {} end function runner.collect_failed_locations(failures, command, scope_kind) return {} end return runner ``` ## Checkliste für neue Runner - is_test_file implementiert - find_nearest implementiert (setzt `spec.file`) - build_command implementiert - build_file_command implementiert - build_all_command implementiert - build_failed_command implementiert - parse_results implementiert - output_parser implementiert (Streaming) - parse_test_output implementiert - collect_failed_locations implementiert - command_spec `{ cmd, cwd }` korrekt zurückgegeben ## Projekt- und Prozessanforderungen - Rolle: **TDD-first Entwickler**. - Jede neue Funktion, jedes neue Kommando und jede Verhaltensänderung muss durch Tests abgesichert sein. - Nach jeder Code-Änderung Tests via `bash run_test.sh` ausführen und bei Fehlern korrigieren, bis alles grün ist. - **Nicht raten**: - Bei unklaren oder mehrdeutigen Anforderungen Arbeit stoppen und Klarstellung verlangen. - TODO/NOTE im Code ist zulässig, stilles Raten nicht. - **Keine stillen Änderungen**: - Bestehende Features dürfen nicht unbemerkt geändert oder ersetzt werden. - Notwendige Anpassungen zur Koexistenz mehrerer Features müssen klar erkennbar sein. - Antworten immer auf Deutsch. - Eine englischsprachige `README.md` ist zu erstellen und wird bei Änderungen automatisch aktualisiert. - TDD-Vorgaben (aus `AGENTS.md`) uebernehmen: - Neue Funktionen/Commands/Verhaltensaenderungen muessen getestet werden. - Tests nach jeder Code-Aenderung ausfuehren. - Im Runner erstellter Quellcode ist ebenfalls zu testen. - Eine eigene `run_test.sh` wird im Runner-Repo angelegt. - Eine Gitea-Action ist zu erstellen, die bei jedem Push die Tests ausfuehrt. - Neovim wird per AppImage installiert (kein `apt`). - Runner laeuft auf `gitea-act-runner` mit Raspberry Pi 5 (ARM). - Beispiel (anpassbarer Workflow): ```yaml name: tests on: push: branches: - "**" jobs: test: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Neovim (AppImage) run: | curl -L -o nvim.appimage https://github.com/neovim/neovim/releases/download/v0.11.4/nvim.appimage chmod +x nvim.appimage sudo mv nvim.appimage /usr/local/bin/nvim - name: Run tests run: bash run_test.sh ``` ## Zusaetzliche Runner-Guidelines (framework-agnostisch) - **Testnamen-Konvention:** Runner sollen eine konsistente, dokumentierte Full-Name-Bildung verwenden (z. B. `Parent/Subtest`), inklusive Mehrfach-Nesting. Diese Konvention muss in `results.*`, `parse_test_output` und `collect_failed_locations` uebereinstimmen. - **TSamNearest-Prioritaet:** Falls moeglich, gelten folgende Regeln: Test-Block > Describe/Context-Block > File-Command. Das Verhalten muss getestet werden (Cursor im Test, zwischen Tests, ausserhalb von Describe/Context). - **Reporter-Payload-Schema:** Wenn ein Custom-Reporter verwendet wird, soll dessen JSON-Payload dokumentiert und stabil sein (z. B. `{ name, status, file, location, output }`), damit Parser/Quickfix/Detail-Output konsistent bleiben. - **Failed-Only-Logik:** Failed-Only muss auf den letzten Fehlermeldungen basieren und nur die fehlerhaften Tests erneut ausfuehren. Die Pattern-Strategie (z. B. Titel-only vs. Full-Name) muss getestet werden. - **CI-Installations-Snippet:** Die Neovim-Installation in CI soll als „authoritative snippet“ behandelt werden und in Runner-Repos 1:1 uebernommen werden.