feat: replace Aider with secure mini-SWE-agent worker
Some checks failed
CI / test (push) Has been cancelled
CI / test (pull_request) Has been cancelled

This commit is contained in:
Christian Meyer
2026-08-31 02:46:58 +02:00
parent 582db3a654
commit 25ed6a8439
30 changed files with 4215 additions and 2148 deletions

191
README.md
View File

@@ -1,170 +1,45 @@
# LAI Automatisierung
# LAI Coding Automation
Kontrollierter Coding-Agent fuer Failure Reports aus
[`lai`](https://gitea.tisinfra.de/christian.meyer/lai). Der Orchestrator verwendet
[Aider](https://aider.chat/) headless als austauschbaren Coding-Kern. Git-, Gitea-,
Policy- und Freigabelogik bleiben in diesem Projekt.
This service turns explicitly queued Gitea issues into reviewed pull requests. It no longer reads the
Laravel production database and does not merge changes automatically.
## Warum Aider?
## Workflow
- scriptbarer Einmal-Auftrag mit `--message-file`
- gute Git- und Repository-Map-Unterstuetzung
- Mistral ueber LiteLLM nutzbar
- automatische Commits koennen deaktiviert werden
- deutlich kleinerer Betriebsumfang als OpenHands
1. LAI creates one Gitea issue for a wrong-answer report and labels it `ai:queued`.
2. This worker replaces the label with `ai:running`, clones `main`, and creates `ai/issue-N`.
3. mini-SWE-agent 2.4.6 implements the issue in a credential-free, unprivileged command environment.
4. The worker rejects protected paths, symlinks, empty patches, and oversized diffs.
5. Deterministic setup and test commands run with isolated test database credentials.
6. A pull request containing `Fixes #N`, the original problem, changed files, and checks is opened.
7. The issue is labeled `ai:ready` or `ai:failed` and receives a status comment.
OpenHands bleibt eine moegliche spaetere Alternative fuer komplexere, mehrstufige
Agentenlaeufe. Sein Headless-Modus arbeitet jedoch immer mit `always-approve`; deshalb
muessen auch dort unsere externen Policy-Gates erhalten bleiben.
Human review and merge are always required.
## Ablauf
## Security boundary
```text
failure_reports(open)
-> transaktionales Claiming (SKIP LOCKED)
-> anonymisierter Agentenauftrag
-> frischer Clone von lai.git
-> Aider-Aenderung ohne Commit
-> Pfad-, Groessen- und Sicherheitsgates
-> Tests
-> Commit
-> PR oder kontrollierter Push auf main
-> failure_reports(resolved/agent_failed)
```
- Gitea and model credentials exist only in the root worker process.
- Agent-issued commands receive a strict environment without inherited worker variables.
- The agent runs as the unprivileged `coder` user.
- Git metadata lives outside the writable worktree, and Git hooks are disabled for commits.
- The Docker socket is not mounted. Repository cloning and pushing use HTTPS with a temporary
`GIT_ASKPASS` credential file that is deleted immediately.
- `.env`, CI, Docker/deployment files, dependencies, generated storage, and symlinks are rejected.
Der Coding-Agent erhaelt keinen Zugang zur ERP- oder Kundendatenbank. Rohdaten und
`.env`-Dateien sind ausgeschlossen. Repository-Pushes verwenden `GIT_ASKPASS`; Tokens
werden nicht in Remote-URLs oder Prozessargumenten gespeichert. Der Orchestrator laeuft
als privilegierter Containerprozess, startet Aider aber als separaten Benutzer `coder`.
Dieser Unterprozess erhaelt nur Modell-Credentials, nicht die Failure-DB- oder
Gitea-Zugangsdaten.
The container is the intended execution boundary. Do not run the worker directly on a developer host.
Die nachgelagerten Testprozesse erhalten separat konfigurierte `TEST_DB_*`-Zugangsdaten.
Dieser Benutzer darf ausschliesslich die isolierten Testschemas bearbeiten. Fuer Laravel
werden daraus `DB_*`, `ERP_DB_*` und `COCKPIT_CONFIG_DATABASE` gesetzt; Aider selbst sieht
diese Werte nie. Produktive ERP- oder Kundenzugangsdaten duerfen hier nicht verwendet werden.
Vor jedem Testlauf erstellt `npm run build` die Vite-Assets und initialisiert
`php artisan migrate:fresh --force` das isolierte Laravel-Schema neu. Deshalb muessen alle
`TEST_DB_*`-Schemas dediziert und vollstaendig entbehrlich sein.
## Configuration
Laravel-Tests laufen mit PHP und Composer direkt im isolierten Worker. Der Docker-Socket
des Hosts wird nicht eingebunden. Abgebrochene Claims werden nach standardmaessig 90
Minuten wieder freigegeben; `STALE_CLAIM_MINUTES` passt dieses Intervall an.
Schlaegt die isolierte Validierung fehl, erhaelt Aider standardmaessig genau einen
Korrekturversuch mit redigierter Testausgabe. `MAX_AGENT_ATTEMPTS` ist auf 1 bis 3 begrenzt.
Copy `.env.example` to `.env` and set `GITEA_TOKEN` plus `MISTRAL_API_KEY`. Existing test database
settings can be retained. Commands are separated with `;;`. Laravel's default connection uses
`TEST_DB_NAME`, the ERP connection uses `TEST_CUSTOMER_DB`, and cross-schema cockpit fixtures use
`TEST_CONFIG_DB`. The worker enables database integration tests and supplies a non-secret
placeholder AI key so mocked provider-discovery tests execute deterministically. Real Gitea and
model credentials are never copied into the test or agent command environment.
## Schnellstart
Start the worker with Docker Compose. Use `lai-agent check` inside the image for configuration
validation, `lai-agent preflight` for clean-clone setup and tests without a model call, or
`lai-agent once` for a single polling cycle.
```bash
cp .env.example .env
# FAILURE_DB_PASSWORD und bei Bedarf MISTRAL_API_KEY eintragen
# TEST_DB_PASSWORD fuer den Benutzer der isolierten Testschemas eintragen
docker compose build
docker compose run --rm automation check
docker compose run --rm automation once
```
Der Default `AUTOMATION_MODE=dry-run` claimt einen offenen Report, erzeugt den
anonymisierten Auftrag und setzt ihn danach wieder auf `open`. Es wird weder ein Agent
gestartet noch gepusht.
## Fortschritt beobachten
```bash
docker compose ps automation
docker compose exec automation lai-agent check
docker compose logs -f automation
```
Der Worker protokolliert nur sichere Metadaten, keine Prompts, SQL-Ergebnisse, Secrets oder
Modellausgaben. Fuer jeden Report erscheinen Workspace-Vorbereitung, Agentenversuch, Anzahl
und Pfade geaenderter Dateien, Policy-Pruefung, jeder Validierungsbefehl, Commit, PR-URL und
Cleanup. Bei laengeren Agenten- oder Testphasen wird alle 30 Sekunden Laufzeitfortschritt
ausgegeben. `agent_running` ohne Heartbeat oder Unterprozess deutet auf einen unterbrochenen
Container hin; normale Claims werden nach `STALE_CLAIM_MINUTES` wieder freigegeben.
## PR-Modus
```dotenv
AUTOMATION_MODE=pr
GITEA_USERNAME=automation-bot
GITEA_TOKEN=...
MISTRAL_API_KEY=...
```
Der Bot benoetigt Schreibzugriff auf `christian.meyer/lai` und die Berechtigung,
Branches sowie Pull Requests anzulegen. Jeder Lauf erstellt
`ai/failure-{report_id}` und einen PR gegen `main`.
## Direkter main-Modus
```dotenv
AUTOMATION_MODE=direct-main
ALLOW_DIRECT_MAIN=true
GITEA_TOKEN=...
```
Der zweite Schalter ist ein Kill-Switch. In diesem Modus sind Migrationen, Routes,
Auth, Middleware, PermissionService und SQLValidatorService zusaetzlich blockiert.
Branch Protection muss den Bot fuer direkte Pushes explizit zulassen. PR-Modus bleibt
die empfohlene Betriebsart.
## Modelle
Das Modell wird ausschliesslich ueber `AGENT_MODEL` gewaehlt. Devstral ist der
Default und ein Coding-Agent, nicht das Chat-Modell der Laravel-Anwendung:
```dotenv
AGENT_MODEL=mistral/devstral-latest
MISTRAL_API_KEY=...
```
Codestral verwendet denselben Mistral-Zugang:
```dotenv
AGENT_MODEL=mistral/codestral-latest
MISTRAL_API_KEY=...
```
Qwen 3.5 27B ueber OpenRouter:
```dotenv
AGENT_MODEL=openrouter/qwen/qwen3.5-27b
OPENROUTER_API_KEY=...
```
Oder mit einem lokalen Ollama auf dem Docker-Host:
```dotenv
AGENT_MODEL=ollama_chat/qwen3.5:27b
OLLAMA_API_BASE=http://host.docker.internal:11434
```
Nach einer Aenderung wird der Worker neu erstellt, damit Compose die neue Umgebung
uebernimmt:
```bash
docker compose up -d --force-recreate automation
docker compose exec -T automation lai-agent check
```
Aider waehlt das passende Edit-Format anhand des Modells. Das Chat-Modell und
Coding-Modell sind bewusst getrennt. Fuer Coding-Agenten sollte ein
agentisch/coding-optimiertes Modell verwendet werden. Ein Wechsel zu OpenHands oder
einem anderen Backend erfordert einen neuen Adapter neben `AiderAgent`.
## Tests
```bash
python -m venv .venv
. .venv/bin/activate
pip install ".[dev]"
ruff check src tests
pytest -q
```
## Repositorys
- Chat: `https://gitea.tisinfra.de/christian.meyer/lai.git`
- Automatisierung: `https://gitea.tisinfra.de/christian.meyer/lai_automatisierung.git`
## Archived implementation
The previous database-polling Aider implementation is preserved unchanged in `old_2026-08-31/`.