Files
qt6-migration-pipeline/app/README.md
T

497 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Qt5 → Qt6 Migrationspipeline
Asynchrone, agentische Pipeline zur automatisierten Migration einer C++/Qt5-Codebasis nach Qt6.
Entwickelt für den Pilotbatch **Q1** (~32 k LOC, 115 Files) als Vorstufe zur vollständigen Q2-Migration (~1,2 Mio LOC, ~6 500 Files).
---
## Inhaltsverzeichnis
1. [Überblick](#überblick)
2. [Architektur](#architektur)
3. [Dual-Track-Strategie](#dual-track-strategie)
4. [Semantisches Chunking](#semantisches-chunking)
5. [Multi-Turn-Konversation](#multi-turn-konversation)
6. [Quality Gates](#quality-gates)
7. [Quarantäne & Tickets](#quarantäne--tickets)
8. [Voraussetzungen](#voraussetzungen)
9. [Einrichtung](#einrichtung)
10. [Docker](#docker)
11. [Ausführung](#ausführung)
12. [Ausgaben](#ausgaben)
13. [Konfigurationsreferenz](#konfigurationsreferenz)
14. [Modulübersicht](#modulübersicht)
15. [Burn-Phase & Skalierung auf Q2](#burn-phase--skalierung-auf-q2)
16. [FAQ](#faq)
---
## Überblick
Die Pipeline übernimmt folgende Aufgaben vollautomatisch:
| Schritt | Beschreibung |
|---|---|
| **Batch-Isolation** | Pro Batch + Track ein eigener Git-Worktree und Build-Verzeichnis |
| **LLM-Migration** | Qt5-APIs werden per LLM (Scaleway) auf Qt6 portiert |
| **Chunk-Verarbeitung** | Dateien > 500 Zeilen werden semantisch gesplittet und parallel migriert |
| **Syntax-Check** | Schneller Per-File-Check via `compile_commands.json` ohne Full-Rebuild |
| **Test-Generierung** | QTest (Qt-naher Code) oder gtest + Smoke-Test pro Datei |
| **Quality Gates** | Build ✓ · Bestehende Tests ✓ · Neue Tests ✓ |
| **Quarantäne** | Dateien, die Gates nicht bestehen, werden isoliert + Ticket erzeugt |
| **Vergleichsreport** | Track A vs. Track B als Markdown + JSON |
---
## Architektur
```
run_migration.py ← CLI-Einstiegspunkt
│
└── migration_pipeline/
├── orchestrator.py ← Batch-Steuerung, beide Tracks parallel
│ ├── git_manager.py ← Worktree + Branch pro Batch×Track
│ ├── build_runner.py ← cmake configure/build/ctest (isoliert)
│ └── migrator.py ← Per-File Agentenloop
│ ├── chunker.py ← Semantisches C++ Chunking
│ ├── api_client.py ← Scaleway API
│ ├── test_generator.py ← QTest / gtest / Smoke
│ ├── quality_gate.py ← Gate-Auswertung
│ └── quarantine.py ← Quarantäne + Ticket
├── reporter.py ← Vergleichsreport A vs B
├── models.py ← Dataclasses & Enums
└── config.py ← Alle Konstanten
```
### Datenfluss pro Datei
```
Quelldatei
│
▼ split_semantic() wenn > 500 Zeilen
[Chunk 1] [Chunk 2] … [Chunk N] ← parallel per LLM migriert
│
▼ Consistency-Pass (Merge-Check via LLM)
Migrierter Code
│
▼ syntax_check_file() (compile_commands.json, kein Full-Rebuild)
│
▼ generate_tests() (QTest oder gtest + Smoke)
│
▼ cmake_configure() + cmake_build() + ctest
│
┌──┴──────────────────┐
│ Quality Gates │
│ ✓ Build │
│ ✓ Bestehende Tests │
│ ✓ Neue Tests │
└──┬──────────────────-┘
│ └── FAIL → Retry (max 3×)
│ └── Quarantäne
▼
git commit (Branch migration/<batch>/<track>)
```
---
## Dual-Track-Strategie
Beide Tracks laufen **gleichzeitig** auf dem selben Batch.
| | Track A – Konservativ | Track B – Modern |
|---|---|---|
| **Ziel** | Qt5→Qt6 Kompatibilität, minimale Änderungen | Qt6 + C++20 Modernisierung |
| **Stil** | Original-Stil beibehalten | Ranges, Concepts, `std::optional`, Lambdas, `[[nodiscard]]` usw. |
| **Git-Branch** | `migration/<batch>/track_a_conservative` | `migration/<batch>/track_b_modern` |
| **Risiko** | Niedrig | Mittel |
Nach Abschluss des Pilotbatches folgt die **Burn-Phase**: beide Tracks werden identischen Gates unterzogen und anhand von Erfolgsquote, Warnungsanzahl und Wartbarkeit verglichen. Der Vergleichsreport enthält eine automatische Empfehlung.
---
## Semantisches Chunking
Große Dateien (> 500 Zeilen) werden **nicht** an willkürlichen Zeilengrenzen getrennt, sondern an:
1. Klassen-/Struct-/Namespace-Definitionen (`class Foo {`, `namespace Bar {`)
2. Funktionsdefinitionen (Top-Level und Methoden)
3. Fallback: nächste Leerzeile unterhalb der 500-Zeilen-Marke
Alle Chunks einer Datei werden **parallel** per API migriert. Anschließend läuft ein **Consistency-Pass** (separater LLM-Aufruf), der:
- doppelte `#pragma once` / Include-Guards entfernt,
- offene Namespaces schließt,
- doppelte Deklarationen bereinigt.
---
## Multi-Turn-Konversation
Die Retry-Schleife des Migrations-Agenten verwendet eine **wachsende Gesprächshistorie**, sodass das Modell beim nächsten Versuch seine eigene fehlerhafte Ausgabe zusammen mit der Compiler-Fehlermeldung sieht – analog zum Verhalten des Copilot CLI.
### Konversationsstruktur
```
Iteration 0: [system, user(„Portiere folgenden Qt5-Code…")]
→ reply_v1
Iteration 1: [system, user(„Portiere…"), assistant(reply_v1),
user(„Fehler im vorherigen Versuch:\n<fehler>\nBitte korrigiere:\n<code>")]
→ reply_v2
```
### Drei unabhängige Konversations-Scopes
| Scope | Lebensdauer | Zweck |
|---|---|---|
| **Whole-file retry** (`whole_messages`) | Pro Datei; wächst über alle Retries | Modell lernt iterativ aus Compiler-Fehlern seiner eigenen Ausgabe |
| **Chunk-Migration** (je Chunk) | Pro Chunk, frisch + unabhängig | Parallele Chunk-Verarbeitung ohne gegenseitige Beeinflussung |
| **Consistency-Pass** (frisch) | Pro Datei, einmalig nach Chunk-Merge | Isoliert vom Retry-Loop; prüft nur Merge-Konsistenz |
### Per-File-Scoping
`whole_messages` ist eine **lokale Variable** innerhalb von `migrate_file` und wird nach Rückgabe der Funktion verworfen. Gesprächshistorie wird **niemals** zwischen Dateien oder Batches geteilt.
### Layered API in `api_client.py`
| Funktion | Typ | Verwendung |
|---|---|---|
| `_send_messages(client, model, messages)` | privat | Einziger HTTP-Aufruf; nimmt eine fertige Message-Liste entgegen |
| `call_api(client, model, system, user)` | öffentlich, stateless | Baut `[system, user]` und delegiert – rückwärtskompatibel |
| `call_api_multiturn(client, model, messages)` | öffentlich, multi-turn | Übergibt die komplette Verlaufsliste direkt an `_send_messages` |
`test_generator.py` verwendet weiterhin das stateless `call_api`; nur `migrator.py` nutzt `call_api_multiturn`.
---
## Quality Gates
| Gate | Bedingung | Blockierend? |
|---|---|---|
| **Build** | `cmake --build` returncode 0 | ✅ Ja |
| **Bestehende Tests** | Vollständiger CTest-Lauf grün | ✅ Ja |
| **Neue Tests** | Generierte Unit- + Smoke-Tests grün | ✅ Ja |
| **Warnungen** | Warnungsanzahl = 0 | ⚠️ Advisory (nicht blockierend) |
Warnungen werden vollständig geloggt und im Vergleichsreport ausgewiesen. Sie blockieren die Pipeline **nicht** (konfigurierbar über `WARNINGS_ARE_BLOCKING=True` in `config.py`).
### Fast Syntax-Check
Vor dem teuren Full-Build wird jede Datei einzeln per `compile_commands.json` (erzeugt bei `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`) syntaxgeprüft:
```
clang++ -fsyntax-only <compile_flags_aus_json> <datei.cpp>
```
Das spart bei einer 1,2-Mio-LOC-Codebasis erheblich Zeit.
---
## Quarantäne & Tickets
Dateien werden in Quarantäne verschoben, wenn:
- **3 Retry-Versuche** erschöpft sind, oder
- das **Zeitbudget** überschritten wurde (10 min für kleine Dateien, 25 min für Dateien > 1 000 Zeilen).
Quarantäne ist **nicht-blockierend**: der Rest des Batches läuft weiter.
### Generierte Artefakte
Für jede quarantänisierte Datei werden zwei Dateien in `quarantine/` erzeugt:
**`<stem>_<track>_<timestamp>.json`** – maschinenlesbar:
```json
{
"file_path": "src/core/MyClass.cpp",
"track": "track_a_conservative",
"failure_class": "compile_error",
"last_error": "error: 'QRegExp' was not declared ...",
"affected_apis": ["QRegExp"],
"chunk_info": "3 chunks, ~500 Zeilen/Chunk",
"manual_recommendation": "Compile-Fehler. Prüfe Qt6-API-Änderungen für: QRegExp.",
"timestamp": "2026-07-08T18:32:12Z",
"retry_count": 3,
"elapsed_seconds": 187.4
}
```
**`<stem>_<track>_<timestamp>.md`** – manuelles Review-Ticket:
```markdown
# Quarantäne-Ticket: MyClass.cpp
| Feld | Wert |
|---|---|
| Fehlerklasse | compile_error |
| Empfehlung | Qt6 Porting Guide: QRegExp … |
…
```
### Fehlerklassen
| Klasse | Bedeutung |
|---|---|
| `compile_error` | Compilerfehler (Qt5-API entfernt / geändert) |
| `link_error` | Linkfehler (fehlende Qt6-Modulabhängigkeit) |
| `test_failure` | Semantische Abweichung in Tests |
| `test_build_failure` | Generierter Test kompiliert nicht |
| `budget_exceeded` | Zeitbudget überschritten |
| `api_error` | Scaleway-API nicht erreichbar |
| `chunk_merge_error` | Inkonsistenz nach Chunk-Merge |
---
## Voraussetzungen
### System
- Python ≥ 3.11
- cmake ≥ 3.22
- clang++ (für `syntax_check_file`-Fallback; optional wenn `compile_commands.json` vorhanden)
- git ≥ 2.35 (für `git worktree`)
- GTest und Qt6 installiert und für cmake auffindbar
### Scaleway API
- Zugang zur [Scaleway AI API](https://www.scaleway.com/en/docs/ai-data/managed-inference/)
- Modelle: `glm-5.2` (große Dateien) · `qwen3.6-35b-a3b` (kleine Dateien)
---
## Einrichtung
```bash
# 1. Abhängigkeiten installieren
pip install -r requirements.txt
# 2. API-Key setzen
echo "SCW_SECRET_KEY=your_key_here" > .env
# 3. Prioritätsliste erstellen (eine Datei pro Zeile, in Bearbeitungsreihenfolge)
cat > q1_files.txt << 'EOF'
# cmake-Dateien zuerst (beeinflussen Build aller anderen)
src/CMakeLists.txt
src/core/CMakeLists.txt
# Header-Dateien
src/core/MyClass.h
src/core/OtherClass.h
# Implementierungsdateien
src/core/MyClass.cpp
src/core/OtherClass.cpp
EOF
```
> **Dateireihenfolge**: Die Pipeline verarbeitet Dateien **exakt in der angegebenen Reihenfolge** (innerhalb des Tracks). Empfohlene Reihenfolge: cmake-Dateien → Header → kleine .cpp → große .cpp.
---
## Docker
Das mitgelieferte `Dockerfile` enthält alle System-Abhängigkeiten (Ubuntu 22.04, Python 3.11, cmake, clang, git) und eignet sich für eine reproduzierbare Ausführung ohne lokale Installation.
### Image bauen & starten (empfohlen: `run_docker.sh`)
```bash
# Build + interaktive Shell (Host-UID/GID werden automatisch gemappt)
./run_docker.sh
# Build + direkte Ausführung der Pipeline
./run_docker.sh python3.11 run_migration.py \
--batch-id q1_pilot \
--file-list ./q1_files.txt
```
Das Skript:
- baut das Image unter dem Namen `migration-pipeline:ubuntu22.04`,
- mountet das aktuelle Verzeichnis als `/app` (Änderungen wirken sofort),
- mappt die Host-UID/GID, sodass erstellte Dateien dem aufrufenden Benutzer gehören,
- vergibt bei Bedarf `sudo`-Rechte im Container.
### Manuelles `docker build` / `docker run`
```bash
# Image bauen
docker build -t migration-pipeline:ubuntu22.04 .
# API-Key übergeben und Pipeline starten
docker run --rm -it \
-v "$(pwd):/app" \
-w /app \
-e SCW_SECRET_KEY="your_key_here" \
migration-pipeline:ubuntu22.04 \
python3.11 run_migration.py \
--batch-id q1_pilot \
--file-list ./q1_files.txt
# Dry-Run (kein API-Aufruf, kein Key erforderlich)
docker run --rm -it \
-v "$(pwd):/app" \
-w /app \
migration-pipeline:ubuntu22.04 \
python3.11 run_migration.py \
--batch-id q1_pilot \
--file-list ./q1_files.txt \
--dry-run
```
### Hinweise
| Thema | Empfehlung |
|---|---|
| **Scaleway-Env** | Benötigt: `SCW_ACCESS_KEY`, `SCW_SECRET_KEY`, `SCW_DEFAULT_ORGANIZATION_ID`, `SCW_DEFAULT_PROJECT_ID`, `SCW_SERVERLESS` (runtime env oder `.env`) |
| **Qt6 / GTest** | Nicht im Image enthalten – das C++-Repository muss bereits konfigurierte cmake-Targets mitbringen oder per Bind-Mount bereitgestellt werden |
| **Image-Name** | Über die Umgebungsvariable `IMAGE_NAME` anpassbar, z.B. `IMAGE_NAME=my-registry/migration-pipeline:1.0 ./run_docker.sh` |
---
## Ausführung
### Dry-Run (kein API-Aufruf)
```bash
python run_migration.py \
--batch-id q1_pilot \
--file-list ./q1_files.txt \
--dry-run
```
Gibt Dateiliste mit Zeilenanzahl und Chunk-Schätzung aus.
### Pilotbatch Q1
```bash
python run_migration.py \
--batch-id q1_pilot \
--file-list ./q1_files.txt \
--model glm \
--repo-root . \
--source-root ./src
```
### Mit Debug-Ausgabe
```bash
python run_migration.py --batch-id q1_pilot --file-list ./q1_files.txt -v
```
### Alle Optionen
```
--batch-id Eindeutiger Name für diesen Batch (z.B. q1_pilot)
--file-list Pfad zur priorisierten Dateiliste
--repo-root Wurzel des git-Repositories (Standard: .)
--source-root Wurzel des C++ Quellbaums (Standard: .)
--model Modellfamilie für API-Aufrufe: glm|qwen (Standard: glm)
--dry-run Nur Dateiliste und Chunk-Schätzung ausgeben
--verbose, -v Debug-Logging aktivieren
```
---
## Ausgaben
```
workspace/
q1_pilot/
track_a_conservative/ ← git worktree, Branch migration/q1_pilot/track_a_conservative
src/... ← migrierte Quelldateien (nach jedem Erfolg committed)
tests_generated/ ← generierte Unit- und Smoke-Tests
build/ ← isoliertes cmake Build-Verzeichnis
track_b_modern/ ← analog für Track B
quarantine/
MyClass_track_a_conservative_20260708T183200Z.json
MyClass_track_a_conservative_20260708T183200Z.md ← Review-Ticket
reports/
q1_pilot_comparison.md ← Track A vs B Vergleich (Markdown)
q1_pilot_comparison.json ← Track A vs B Vergleich (maschinenlesbar)
```
### Beispiel-Konsolenausgabe
```
================================================================
Batch: q1_pilot
================================================================
Track A (konservativ): 108/115 OK | 7 quarantined | 23 warnings | avg 142s
Track B (modern) : 97/115 OK | 18 quarantined | 41 warnings | avg 198s
================================================================
```
---
## Konfigurationsreferenz
Alle Werte in `migration_pipeline/config.py`:
| Variable | Standard | Beschreibung |
|---|---|---|
| `MODEL_GLM` | `glm-5.2` | Modell-ID der GLM-Familie |
| `MODEL_QWEN` | `qwen3.5-397b-a17b` | Modell-ID der QWEN-Familie |
| `DEFAULT_MODEL_FAMILY` | `glm` | Standard-Auswahl für `--model` |
| `LARGE_FILE_THRESHOLD` | `1000` | Zeilengrenze für Time-Budget (klein/groß) |
| `MAX_CONCURRENT_TASKS` | `3` | Parallele Worker pro Track |
| `CHUNK_SIZE_LINES` | `500` | Max. Zeilen pro Chunk |
| `SOFT_BUDGET_PER_FILE` | `600` s | Soft-Budget (Warnung, kein Abbruch) |
| `HARD_BUDGET_SMALL_FILE` | `600` s | Hard-Budget für kleine Dateien |
| `HARD_BUDGET_LARGE_FILE` | `1500` s | Hard-Budget für große Dateien |
| `MAX_RETRIES` | `3` | Max. Versuche pro Datei und Track |
| `WARNINGS_ARE_BLOCKING` | `False` | `True` = Warnungen blockieren Gate |
| `WORKSPACE_ROOT` | `workspace/` | Verzeichnis für git-Worktrees |
| `QUARANTINE_DIR` | `quarantine/` | Quarantäne-Artefakte |
| `REPORTS_DIR` | `reports/` | Vergleichsreports |
---
## Modulübersicht
| Modul | Verantwortung |
|---|---|
| `config.py` | Zentralkonfiguration, Konstanten, Umgebungsvariablen |
| `models.py` | Dataclasses (`FileResult`, `BatchResult`, `QuarantineEntry` usw.), Enums |
| `chunker.py` | Semantisches C++ Chunking an Klassen-/Funktionsgrenzen |
| `api_client.py` | Scaleway API Client (async, `httpx`); layered API: stateless `call_api` + multi-turn `call_api_multiturn` |
| `build_runner.py` | cmake configure/build, CTest, `compile_commands.json`-Parser |
| `test_generator.py` | QTest/gtest/Smoke-Generierung, idempotente CMakeLists-Registrierung |
| `quality_gate.py` | Aggregierte Gate-Auswertung (Build · bestehende Tests · neue Tests) |
| `quarantine.py` | Fehlerklassifikation, Qt5-API-Erkennung, JSON+Markdown-Ticket |
| `git_manager.py` | `git worktree` Lifecycle, Per-File-Commits |
| `migrator.py` | Per-File Agentenloop: Chunk-Strategie, Multi-Turn-Retry-Konversation, Budget-Kontrolle |
| `orchestrator.py` | Batch-Orchestrierung, Dual-Track parallel, cmake-Konfiguration |
| `reporter.py` | Track A vs B Vergleichsreport (Markdown + JSON) |
| `run_migration.py` | CLI, Argument-Parsing, Dry-Run |
---
## Burn-Phase & Skalierung auf Q2
### Burn-Phase (nach Q1)
1. Beide Branches (`track_a_conservative`, `track_b_modern`) auf identischer Testinfrastruktur deployen.
2. Stabilität, Warnungsfreiheit und Wartbarkeit vergleichen (Report `q1_pilot_comparison.md`).
3. Gewinner-Track als Basis für Q2 wählen (oder hybride Strategie: Track A für risikoarme Module, Track B für gut getestete Module).
### Skalierung auf Q2
| Parameter | Q1 (Pilot) | Q2 (Vollmigration) |
|---|---|---|
| Dateien | ~115 | ~6 500 |
| LOC | ~32 000 | ~1 200 000 |
| Empfohlene Worker | 3 | 5–8 (anpassen je API-Rate-Limit) |
| Batches | 1 | ~57 (à 115 Files) |
| Erwartete Laufzeit* | ~4–6 h | ~200–300 h gesamt |
> *Schätzung bei 3 Workern, avg. 150 s/Datei, 80 % Erfolgsquote.
Empfohlene Batch-Größe für Q2: **100–150 Files** (ca. 30 k LOC), sortiert nach Abhängigkeitstiefe (cmake → Header → Implementierung).
---
## FAQ
**Warum git worktrees statt direktem Arbeiten im Repository?**
Damit Track A und Track B niemals denselben Dateisystem- oder Build-Zustand teilen. Async-Worker würden sich sonst gegenseitig überschreiben.
**Was passiert mit quarantänierten Dateien nach dem Batch?**
Sie werden wiederhergestellt (Original bleibt erhalten), das Ticket landet in `quarantine/`. Die Datei ist mit Fehlerklasse, betroffenen APIs und Empfehlung indiziert und kann gezielt manuell migriert werden.
**Kann ich nur Track A oder nur Track B laufen lassen?**
Aktuell nicht über CLI-Flag. Einfachste Lösung: `TRACK_B_ID` in `orchestrator.py` auskommentieren. Ein `--track` CLI-Parameter ist als spätere Erweiterung vorgesehen.
**Was tun bei Rate-Limiting der Scaleway API?**
`MAX_CONCURRENT_TASKS` in `config.py` reduzieren. Alternativ Exponential-Backoff in `api_client.py` ergänzen.
**Sind die generierten Tests vollständig korrekt?**
Nein – sie sind ein Ausgangspunkt. Die Quality Gates stellen sicher, dass sie *kompilieren und laufen*, aber keine logischen Fehler enthalten. Manuelle Nachkontrolle empfohlen, besonders für fachkritische Module.
**Wie wird mit `.cmake`-Dateien umgegangen?**
`.cmake`-Dateien werden wie C++ Dateien per LLM migriert (cmake-spezifische Qt5→Qt6 Syntax). Da sie nicht kompiliert werden, entfällt der Syntax-Check; die Gates Build/Test gelten nach dem nächsten Full-Build.