# 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//) ``` --- ## 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//track_a_conservative` | `migration//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\nBitte korrigiere:\n")] → 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 ``` 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: **`__.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 } ``` **`__.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.