Files
qt6-migration-pipeline/app
..

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
  2. Architektur
  3. Dual-Track-Strategie
  4. Semantisches Chunking
  5. Multi-Turn-Konversation
  6. Quality Gates
  7. Quarantäne & Tickets
  8. Voraussetzungen
  9. Einrichtung
  10. Docker
  11. Ausführung
  12. Ausgaben
  13. Konfigurationsreferenz
  14. Modulübersicht
  15. Burn-Phase & Skalierung auf Q2
  16. 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:

{
  "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:

# 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
  • Modelle: glm-5.2 (große Dateien) · qwen3.6-35b-a3b (kleine Dateien)

Einrichtung

# 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)

# 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

# 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)

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

python run_migration.py \
    --batch-id q1_pilot \
    --file-list ./q1_files.txt \
    --model glm \
    --repo-root . \
    --source-root ./src

Mit Debug-Ausgabe

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.