Compare commits

...
25 Commits
Author SHA1 Message Date
Christoph Schwörer 7df384f6d2 Tag 1 abgeschlossen 2026-08-26 08:19:33 +02:00
Christoph Schwörer f045b99a25 Codebasis als Dateien ins Arbeitsrepo statt als Gitlink
QuellCode/CentronERP war nur als Gitlink (Submodul-Referenz auf 79c1142)
getrackt, ohne .gitmodules und ohne erreichbares Remote. Der
Untersuchungsgegenstand der Versuchsreihe war damit nicht reproduzierbar
gesichert: Ein Klon haette ein leeres Verzeichnis erhalten, und die Belege
der 3.287 Anforderungen waeren nicht ueberpruefbar gewesen.

Umstellung:
- Historie nach c:\DEV\CentronERP_git_snapshot_79c1142 ausgelagert
  (vollstaendig lesbar, enthaelt 79c1142 und Vorgaenger 89ccfd6)
- Gitlink aus dem Index entfernt
- Dateiinhalt aufgenommen: 24.557 Dateien, rund 333 MB

Die verschachtelte .gitignore der Codebasis gilt weiter, Build-Artefakte
bleiben ausgeschlossen. Details in Versuche/Versuch_01/_Codebasis-Nachweis.md
2026-08-26 07:43:51 +02:00
Christoph Schwörer 18edae75b6 V01 Erste Runs 2026-08-26 07:34:29 +02:00
ChristophSchwoerer 0ec86aed8c lit.bib fixed 2026-08-25 10:39:25 +02:00
Gowler 714dab3986 sync 2026-08-25 10:20:31 +02:00
Gowler 376972453b Merge branch 'master' of http://192.168.0.99:3011/Gowler/Masterarbeit 2026-08-25 10:18:41 +02:00
Gowler 8f204bc330 reorganize Kapitel 2026-08-25 10:18:38 +02:00
ChristophSchwoerer dbd4125791 Reorganzied Versuche 2026-08-13 11:26:44 +02:00
Gowler 82a9a37deb 01-04 fertig 2026-05-31 07:52:45 +02:00
Gowler 776bd785e3 04_einmal durch 2026-05-30 10:11:37 +02:00
Gowler 43a32779d8 Zwischenstand 04 und 01 2026-05-29 09:39:40 +02:00
Gowler d4c2a0d269 Überarbeitung layout und Kap 4 2026-05-29 08:57:08 +02:00
Gowler 2bd9646f44 04_init neu 2026-05-26 08:43:03 +02:00
Gowler 7ab5d923f8 03_fertig 2026-05-25 09:35:11 +02:00
Gowler 26b257bc95 Save Variante für Kap 4-6 2026-05-25 09:34:38 +02:00
Gowler cc71e4e935 03_z71 2026-05-10 19:07:50 +02:00
Gowler aa03af844f 02_03 fertig 2026-05-10 13:37:04 +02:00
centron\schwoerer 26aaeec7dc 02_03 z42 2026-05-04 19:24:14 +02:00
centron\schwoerer 2ccc4245d3 02_02 fertig 2026-04-21 10:57:04 +02:00
centron\schwoerer 715a7c0782 02_02 235 2026-04-17 14:13:32 +02:00
centron\schwoerer 76cc49f8f0 02_02_ LLms Struktur 2026-04-17 11:47:42 +02:00
centron\schwoerer 656978f999 02_01 fertig 2026-04-16 13:21:40 +02:00
centron\schwoerer 52b00ddec5 RQ bis Z77 2026-04-16 11:09:35 +02:00
centron\schwoerer 3dea09add1 Kap 2 Aufgeteilt. Überarbeitet bis Zeile 28 2026-03-10 16:27:15 +01:00
centron\schwoerer 313c4a416d Commit 2026-03-10 13:35:05 +01:00
25309 changed files with 6063503 additions and 2209 deletions
+804
View File
@@ -0,0 +1,804 @@
---
name: run-experiment
description: Führt einen Versuchs-Prompt aus einer Prompt-Datei als messbaren Headless-Lauf aus (claude -p) und schreibt ein Messprotokoll mit Start-/Endzeit, Modell, Tokenverbrauch und weiteren Metriken. Verwenden bei "/run-experiment <Pfad-zur-Prompt-Datei>" oder wenn der User einen Versuch/ein Experiment ausführen und tracken will.
argument-hint: <Pfad zur Prompt-Datei> <Root-Verzeichnis>
version: 4.0.0
---
# RunExperiment – Versuchslauf mit Messprotokoll
Du führst einen wissenschaftlichen Versuchslauf für die Masterarbeit aus. Der Prompt aus der
übergebenen Datei wird als **separater Headless-Lauf** von Claude Code ausgeführt, damit
Tokenverbrauch und Modell exakt und maschinenlesbar erfasst werden. Anschließend
schreibst du ein Messprotokoll neben die Prompt-Datei.
## Parameter
- **Prompt** (Pflicht): Pfad zur Prompt-Datei (Markdown). Erstes Argument in `$ARGUMENTS`.
- **Root** (Pflicht): Verzeichnis, das als Root des Versuchslaufs dient. Zweites Argument.
Der Headless-Lauf wird mit diesem Verzeichnis als Arbeitsverzeichnis gestartet – es ist
die Wurzel der zu analysierenden Codebasis und wird nur GELESEN. Fehlt das Argument: den
User danach fragen und NICHT stillschweigend das aktuelle Verzeichnis verwenden. Vor dem
Lauf prüfen, dass das Verzeichnis existiert; sonst abbrechen und den User informieren.
- **Modell** (Pflicht, **kein Default**): Wird **vor jedem Lauf beim User erfragt** – siehe
Schritt 4 im Ablauf. Nennt der User das Modell bereits im Aufruf („… mit Opus 5"), diese
Angabe übernehmen und **nicht** erneut fragen. Ohne Angabe niemals stillschweigend ein
Modell wählen: Das Modell ist eine unabhängige Variable der Versuchsreihe.
- **Agentenmodus** (Pflicht, **kein Default**): Wird wie das Modell **vor jedem Lauf beim User
erfragt** – siehe Schritt 5 im Ablauf. Bestimmt, ob und welche Subagenten der Lauf einsetzen
darf. Drei Modi: `solo`, `builtin`, `custom` (Details in Schritt 5).
- **Effort** (Pflicht, **kein Default**): Denkaufwand des Modells. Wird wie Modell und
Agentenmodus **vor jedem Lauf beim User erfragt** – siehe Schritt 6.
Alle Ausgaben des Laufs (Ergebnisdateien, Messprotokoll, Rohdaten) landen in einem **neuen
Laufverzeichnis neben der Prompt-Datei**, niemals im Root-Verzeichnis:
```
<Verzeichnis der Prompt-Datei>\
<NN>_Prompt.md
<ModellID>\<Agentenmodus>\<Effort>\
<NN>_Lauf_<yyyy-MM-dd_HHmmss>_v<skillversion>-<id4>\
Protokoll.md
RawResult.json
Stderr.log
_meta\ (Laufsteuerung: combined_prompt.md, before.txt, after.txt, Zeitstempel,
subagenten.md/.json, anforderungen.md/.json)
Ergebnisse\ (NUR vom Prompt erzeugte Artefakte, z. B. StRS.md, SyRS.md, ...)
```
**Die unabhängigen Variablen bilden die Ordnerebenen, nicht den Dateinamen:**
- `<ModellID>` – die an `--model` übergebene ID, z. B. `claude-sonnet-5`, `claude-opus-5`,
`claude-fable-5`
- `<Agentenmodus>` – `solo`, `builtin` oder `custom`
- `<Effort>` – `low`, `medium`, `high`, `xhigh` oder `max`
Der Verzeichnisname trägt nur noch, was den einzelnen Lauf identifiziert:
- `<NN>` – Nummernpräfix der Prompt-Datei (z. B. `01` bei `01_Prompt.md`)
- `<yyyy-MM-dd_HHmmss>` – Startzeit **sekundengenau**
- `<skillversion>` – Version dieses Skills zum Startzeitpunkt, z. B. `v3.10.0`
- `<id4>` – 4 Hexstellen Zufall; garantiert Eindeutigkeit auch bei gleichzeitigem Start
**Warum Ordnerebenen statt Namensbestandteile:** Der Name wuchs mit jeder neuen unabhängigen
Variable weiter und war mit fünf Bestandteilen kaum noch lesbar. Als Ordnerebenen sind die
Bedingungen navigierbar, je Zelle abzählbar (`ls <modell>/<modus>/<effort> | wc -l` ergibt
unmittelbar die Anzahl der Messpunkte) und beim Hinzufügen einer weiteren Variable erweiterbar,
ohne bestehende Namen zu brechen. Die Angaben sind **redundant zum Protokoll** – bei Widerspruch
gilt das Protokoll.
**Jeder Lauf ist vollständig selbstenthalten.** Sämtliche Steuerdateien liegen in `_meta\`
**innerhalb** des Laufverzeichnisses – nicht im gemeinsamen Scratchpad. Das ist Voraussetzung
für parallele Läufe (siehe „Parallele Läufe") und archiviert nebenbei den exakt gesendeten
Prompt je Lauf. `Ergebnisse\` enthält ausschließlich die vom Prompt geforderten Artefakte.
## Aufbau dieses Skills
Der Skill ist zweigeteilt:
- **`## Prozess`** beschreibt werkzeugneutral, **was** je Lauf zu tun und zu erheben ist. Dieser
Teil gilt unabhängig davon, mit welchem LLM oder welcher CLI gearbeitet wird.
- **`## Werkzeugadapter`** beschreibt **wie** das mit einem konkreten Werkzeug umgesetzt wird.
Derzeit ist nur der Adapter für Claude Code ausgearbeitet.
**Arbeitsteilung mit der Prompt-Datei:** Der Prompt enthält ausschließlich die *Analyseanweisung*
und ist damit von jedem LLM verwendbar. Alles Werkzeug- und Ablaufbezogene – verfügbare
Werkzeuge, Ausgabeverzeichnis, Messgrößen – steuert dieser Skill bei und dokumentiert es im
Protokoll. Die Prompt-Datei wird dabei **nie verändert**; ergänzende Blöcke werden nur an den
per stdin übergebenen Text angehängt (siehe Schritt „Prompt zusammenstellen").
## Prozess
### 1. Vorbereitung
1. Prompt-Datei lesen. Existiert sie nicht: abbrechen und den User informieren.
2. Aus dem Metadaten-Block der Prompt-Datei (falls vorhanden) Versuch/Iteration übernehmen.
3. **CLI-Pfad auflösen.** Unter Windows liegt `claude` in der Regel **nicht im PATH**. Erst
`Get-Command claude -ErrorAction SilentlyContinue` versuchen; schlägt das fehl, auf die
VSCode-Extension zurückfallen und die höchste Versionsnummer wählen:
```powershell
$claude = (Get-Command claude -ErrorAction SilentlyContinue).Source
if (-not $claude) {
$claude = Get-ChildItem "$env:USERPROFILE\.vscode\extensions\anthropic.claude-code-*-win32-x64\resources\native-binary\claude.exe" |
Sort-Object FullName | Select-Object -Last 1 -ExpandProperty FullName
}
```
Findet sich keine ausführbare Datei: abbrechen und den User informieren. Den aufgelösten
Pfad fürs Protokoll festhalten.
4. **Modell beim User erfragen.** Hat der User das Modell nicht bereits im Aufruf genannt,
**immer** per `AskUserQuestion` nachfragen – auch dann, wenn frühere Läufe derselben
Versuchsreihe ein bestimmtes Modell verwendet haben. Es gibt bewusst keinen Default.
Als Optionen die vollen Modell-IDs anbieten, nicht die Kurzformen:
| Modell-ID | Einordnung |
|---|---|
| `claude-opus-5` | stärkstes Modell, teuerster Lauf |
| `claude-sonnet-5` | 1-Mio.-Kontextfenster, für große Codebasen meist ausreichend |
| `claude-fable-5` | schnell und günstig |
| `claude-haiku-4-5-20251001` | günstigstes Modell |
In der Frage den letzten verwendeten Stand nennen, damit der User bewusst wechseln oder
bewusst wiederholen kann (z. B. „Lauf 3 lief mit `claude-sonnet-5`, 11.516.200 Tokens, 23:40").
**Niemals die Kurzformen `opus`, `sonnet`, `fable` an `--model` übergeben.** Sie lösen auf
das jeweils neueste Modell auf und verschieben sich über die Zeit – damit wäre die
Versuchsbedingung nicht reproduzierbar. Für ein 1-Mio.-Token-Fenster die Variante mit
`[1m]`-Suffix wählen (z. B. `claude-opus-5[1m]`).
Weicht das gewählte Modell vom letzten Lauf ab, im Protokoll unter „Anmerkungen" als
geänderte Versuchsbedingung vermerken und in der Änderungshistorie des Skills ergänzen.
5. **Agentenmodus beim User erfragen.** Wie beim Modell: kein Default, immer nachfragen, wenn
der User den Modus nicht bereits im Aufruf genannt hat. Der Modus ist die zentrale
Unterscheidung zwischen den Versuchen der Arbeit.
| Modus | Versuch | Bedeutung |
|---|---|---|
| `solo` | **V1** | Ein Thread, ein Kontext. Der Lauf darf **keine** Subagenten starten. |
| `builtin` | **V1b** | Eingebaute Subagenten erlaubt (`Explore`, `general-purpose`). |
| `custom` | **V2** | Nur **vordefinierte, geprompte Agenten** aus `--agents`. |
Flags je Modus – zusätzlich zur Standardkonfiguration aus Abschnitt 2:
```powershell
switch ($modus) {
'solo' { $agentFlags = @('--disallowedTools','Task','Agent','Workflow') }
'builtin' { $agentFlags = @() }
'custom' { $agentFlags = @('--agents', (Get-Content $agentDatei -Raw)) }
}
```
**Warum `Task`, `Agent` *und* `Workflow`:** Welchen Namen das Subagenten-Werkzeug in der
jeweiligen CLI-Version trägt, ist nicht dokumentiert – im Binary von 2.1.245 kommen `Task`
und `Agent` vor; beide zu sperren ist unschädlich, ein nicht existierender Name läuft ins
Leere. `Workflow` orchestriert ebenfalls Subagenten und muss mitgesperrt werden: Im
Verifikationstest (2026-08-25, CLI 2.1.245) wich der Agent nach der Sperre von `Task`/`Agent`
**genau darauf** aus. Verifiziert wurden `spawned: 0`, die Antwort „KEINE SUBAGENTEN
MOEGLICH" und ein Denial auf `Workflow`. Kontrolle nach dem Lauf: `subagent_stats.spawned` **muss** im
Modus `solo` genau `0` sein – andernfalls hat die Sperre nicht gegriffen und der Lauf ist
als Fehlmessung zu kennzeichnen.
Im Modus `solo` sind **Permission-Denials auf `Task`/`Agent` erwartbar**, wenn der Agent zu
delegieren versucht. Sie sind im Protokoll getrennt von sonstigen Denials auszuweisen und
**nicht** als Einschränkung der Werkzeugkonfiguration zu werten – sie sind die Bedingung selbst.
Für `custom` (V2): Die Agentendefinitionen liegen als JSON-Datei **neben der Prompt-Datei**
(z. B. `Versuche/Versuch_02/02_Agents.json`), **nicht** im Codebasis-Snapshot. Sie werden wie
der Prompt per SHA-256 gehasht und im Protokoll geführt. So bleibt der eingefrorene Snapshot
unangetastet und die Agentenkonfiguration ist eine dokumentierte, versionierte
Versuchsbedingung. Format siehe `claude --help` zu `--agents`.
6. **Effort beim User erfragen.** Kein Default, immer nachfragen, wenn der User die Stufe nicht
bereits im Aufruf genannt hat. Stufen: `low`, `medium`, `high`, `xhigh`, `max`.
Übergabe per `--effort <stufe>`.
In der Frage den bisherigen Stand nennen. **Alle Läufe bis einschließlich Lauf B liefen auf
`high`** – geerbt aus der Sitzungseinstellung, nicht bewusst gesetzt; der Wert wurde
nachträglich aus den Transkripten belegt. Wer die Stufe wechselt, ändert die
Versuchsbedingung und macht den Lauf mit den bisherigen unvergleichbar.
**Wichtig – der Effort ist aus `RawResult.json` nicht rekonstruierbar.** Das Ergebnisobjekt
enthält kein `effort`-Feld. Nur das Session-Transkript hält ihn je Nachricht fest
(`"effort": "high"`). Fehlt er im Protokoll, ist die Bedingung nachträglich allein über das
Transkript belegbar – und das nur, solange die Session persistiert ist.
7. Messgrößen VOR dem Lauf erfassen (PowerShell):
- Startzeit: `Get-Date -Format o`
- SHA-256 der Prompt-Datei: `(Get-FileHash <datei> -Algorithm SHA256).Hash`
- Claude-Code-Version: `& $claude --version`
- Git-Zustand des Root-Verzeichnisses: `git -C <root> rev-parse HEAD` und
`git -C <root> status --porcelain` (dirty ja/nein)
- Git-Commit dieses Repos (Stand der Prompt-Datei): `git rev-parse HEAD`
8. **Kollisionsfreies Laufverzeichnis anlegen.** Sekundengenau, mit Agentenmodus und
Zufalls-ID; bei Namenskollision neu würfeln:
```powershell
# Bedingungen werden Ordnerebenen, nicht Namensbestandteile
$zelle = Join-Path (Join-Path $modell $modus) $effort
$skillVer = 'v4.0.0' # entspricht version: im Frontmatter dieses Skills
do {
$id4 = '{0:x4}' -f (Get-Random -Maximum 65536)
$lauf = Join-Path "<promptverzeichnis>\$zelle" "<NN>_Lauf_$(Get-Date -Format 'yyyy-MM-dd_HHmmss')_${skillVer}-$id4"
} while (Test-Path $lauf)
New-Item -ItemType Directory -Force "$lauf\Ergebnisse" | Out-Null
New-Item -ItemType Directory -Force "$lauf\_meta" | Out-Null
```
9. Dateibestand des Roots vor dem Lauf sichern – das Root soll unverändert bleiben, jede
Änderung dort ist eine Auffälligkeit fürs Protokoll. Ziel ist `_meta\` **im Laufverzeichnis**,
nicht der gemeinsame Scratchpad:
```powershell
# Leerwertsicher: -Value erzwingt das Schreiben auch bei sauberem Root.
Set-Content -Path "$lauf\_meta\before.txt" -Value (git -C <root> status --porcelain | Out-String)
```
**Nicht** `git ... | Set-Content <datei>` verwenden: Ist die Pipeline leer (sauberes Root),
schreibt `Set-Content` die Datei nicht und lässt einen alten Inhalt stehen. Der
Vorher/Nachher-Vergleich meldet dann eine Abweichung, die es nicht gibt.
(Kein Git-Repo? Dann rekursive Dateiliste mit LastWriteTime nach `_meta\` schreiben.)
**Isolation: Die Codebasis ist ein eingefrorener Snapshot ohne KI-Konfigurationen.**
Der Untersuchungsgegenstand wurde einmalig um alle KI-Assistenz-Konfigurationen bereinigt
(`.claude/`, `.agents/`, `.codex/`, `.cursor/`, `.serena/`, `.opencode/`, `.memory-mcp/`,
`CLAUDE.md`, `AGENTS.md`, `.mcp.json`, `opencode.json`, `skills-lock.json`, `.coderabbit.yaml`,
`.aiignore`, `.claudeignore`, `.cursorignore`), und das GitHub-Remote ist entkoppelt. Die
Bedingung „keine Agentendateien, keine MCP-Server" ist damit eine **Eigenschaft des Snapshots**
und muss nicht pro Lauf hergestellt werden. Damit entfällt jeder destruktive Vorbereitungsschritt.
Vor jedem Lauf den Snapshot **prüfen** – nicht bereinigen:
```powershell
$muster = 'CLAUDE\.md|AGENTS\.md|GEMINI\.md|\.mcp\.json|opencode\.json|skills-lock\.json|' +
'\.coderabbit\.yaml|\.aiignore|\.claudeignore|\.cursorignore|copilot-instructions|' +
'^\.(claude|agents|codex|cursor|serena|opencode|memory-mcp|continue|gemini|windsurf)/'
$fund = git -C <root> ls-files | Select-String -Pattern $muster
if ($fund) { "WARNUNG: KI-Konfigurationen im Snapshot:"; $fund }
```
**Den Regex nicht „vereinfachen".** Die Anker (`(^|/)`, `$`, führender Punkt) sind notwendig.
`Select-String` arbeitet in PowerShell standardmäßig **case-insensitiv**; ein gelockertes Muster
schlägt deshalb auf echte Produktdateien an. In dieser Codebasis etwa:
`src/backend/Centron.BL/ArtificialIntelligence/Chat/ClaudeCodeChatModelClient.cs` und
`GoogleGeminiChatModelClient.cs` – Bestandteile des KI-Assistenz-Features der ERP-Suite und
damit legitimer Untersuchungsgegenstand, keine Werkzeugkonfiguration. Bei einem Treffer immer
erst die konkreten Pfade ansehen, bevor Alarm ausgelöst wird.
Findet die Prüfung echte Treffer: **abbrechen und den User informieren – nicht eigenmächtig löschen.**
Der Codebasis-Snapshot ist ein Messinstrument. Eine Änderung daran ist eine Änderung der
Versuchsbedingung und macht Läufe untereinander unvergleichbar; sie gehört abgestimmt,
committet und im Protokoll dokumentiert.
**Das Remote ist entkoppelt.** Im Codebasis-Repo wird niemals gepusht, gefetcht oder ein Remote
hinzugefügt. Der Snapshot bleibt eingefroren.
### 2. Prompt zusammenstellen
Dem Prompttext werden **zwei** Blöcke angehängt. Die Prompt-Datei selbst bleibt unverändert –
ergänzt wird nur der per stdin übergebene Text, und dieser wird als `_meta\combined_prompt.md`
archiviert.
**Block 1 – Werkzeugkontext.** Der Prompt trifft bewusst keine Annahme darüber, welche Werkzeuge
zur Verfügung stehen. Diese Bedingung stellt der Versuchsaufbau bei; sie ist die untersuchte
unabhängige Variable und gehört deshalb hierher, nicht in die Analyseanweisung:
```
### Werkzeugkontext (vom Versuchsaufbau vorgegeben)
Für diesen Lauf stehen zur Verfügung: <Auflistung>.
Nicht verfügbar sind: <Auflistung>.
Triff keine Annahmen über weitere Werkzeuge und versuche nicht, nicht verfügbare
Werkzeuge zu ersetzen.
```
Der Inhalt richtet sich nach dem Agentenmodus:
| Modus | „Zur Verfügung" | „Nicht verfügbar" |
|---|---|---|
| `solo` | Lesen, Suchen und Ausführen von Kommandozeilenbefehlen im Arbeitsverzeichnis | Subagenten, spezialisierte Agentenrollen, externe Werkzeugserver |
| `builtin` | zusätzlich die werkzeugeigenen Subagenten | spezialisierte Agentenrollen aus Konfigurationsdateien, externe Werkzeugserver |
| `custom` | zusätzlich die beigestellten Agentenrollen (namentlich nennen) | externe Werkzeugserver, sofern nicht Teil des Versuchs |
**Block 2 – Ausgabeverzeichnis.** Damit die Ergebnisse im Laufverzeichnis landen und nicht in
der Codebasis:
```
### Ausgabeverzeichnis (überschreibt anderslautende Pfadangaben oben)
Schreibe ALLE zu erzeugenden Ergebnisdateien in das Verzeichnis
`<absoluter Pfad zum Laufverzeichnis>\Ergebnisse\`.
Verändere keine Dateien im Arbeitsverzeichnis (der analysierten Codebasis).
```
### 3. Ausführung (Headless-Lauf)
Dann den kombinierten Prompt per stdin an `claude -p` übergeben. `--add-dir` gibt dem
Headless-Lauf Schreibrecht auf das Laufverzeichnis außerhalb seines Arbeitsverzeichnisses.
Versuchsläufe können lange dauern – **immer als Background-Task starten** (`run_in_background`),
nicht mit Foreground-Timeout arbeiten:
```powershell
$lauf = "<absoluter Pfad zum Laufverzeichnis>"
$prompt = (Get-Content "<prompt-datei>" -Raw) + "`n`n<Ausgabe-Anweisung>"
# Steuerdateien IMMER ins Laufverzeichnis - nie in den gemeinsamen Scratchpad (parallelfaehig)
Set-Content -Path "$lauf\_meta\combined_prompt.md" -Value $prompt -Encoding utf8
Set-Content -Path "$lauf\_meta\startzeit.txt" -Value (Get-Date -Format o)
# Schreibende und bauende Shell-Kommandos sperren – die Codebasis wird nur gelesen.
$denyBash = @(
'Bash(rm:*)','Bash(rmdir:*)','Bash(mv:*)','Bash(cp:*)','Bash(dd:*)',
'Bash(truncate:*)','Bash(chmod:*)','Bash(chown:*)','Bash(ln:*)','Bash(tee:*)',
'Bash(sed -i:*)','Bash(git checkout:*)','Bash(git restore:*)','Bash(git clean:*)',
'Bash(git reset:*)','Bash(git add:*)','Bash(git commit:*)','Bash(git push:*)',
'Bash(dotnet:*)','Bash(msbuild:*)','Bash(npm install:*)','Bash(nuget:*)'
)
$denyPs = @(
'PowerShell(Remove-Item:*)','PowerShell(Move-Item:*)','PowerShell(Copy-Item:*)',
'PowerShell(New-Item:*)','PowerShell(Set-Content:*)','PowerShell(Add-Content:*)',
'PowerShell(Clear-Content:*)','PowerShell(Out-File:*)','PowerShell(Set-ItemProperty:*)',
'PowerShell(dotnet:*)','PowerShell(msbuild:*)'
)
Set-Location "<root>"
Get-Content "$lauf\_meta\combined_prompt.md" -Raw |
& $claude -p `
--output-format json `
--safe-mode `
--strict-mcp-config `
--permission-mode acceptEdits `
--allowedTools "Bash" "PowerShell" `
--disallowedTools @($denyBash + $denyPs) `
--model <vom User gewaehlte Modell-ID aus Schritt 4> `
--effort <vom User gewaehlte Stufe aus Schritt 6> `
@agentFlags `
--add-dir "$lauf" `
2> "$lauf\Stderr.log" | Set-Content "$lauf\RawResult.json"
Set-Content -Path "$lauf\_meta\endzeit.txt" -Value (Get-Date -Format o)
```
Bedeutung der Flags:
| Flag | Zweck |
|---|---|
| `--safe-mode` | Isolation: CLAUDE.md, Skills, Plugins, Hooks, MCP-Server, Custom-Agenten, Commands und Output-Styles aus – ohne Dateieingriff. Auth, Modellwahl, eingebaute Tools und Permissions bleiben normal aktiv. |
| `--strict-mcp-config` | zweite Absicherung gegen MCP-Server aus Projekt- oder User-Konfiguration |
| `--permission-mode acceptEdits` | Schreibrechte für die Ergebnisdateien im Laufverzeichnis |
| `--allowedTools "Bash" "PowerShell"` | **Shell-Zugriff ohne Rückfrage** – Standard seit Iteration 02 |
| `--disallowedTools <Denylist>` | schreibende und bauende Kommandos gesperrt; Deny hat Vorrang vor Allow |
| `--add-dir "$lauf"` | Schreibziel außerhalb des Arbeitsverzeichnisses |
| `@agentFlags` | Agentenmodus aus Schritt 5: `solo` sperrt `Task`/`Agent`, `builtin` fügt nichts hinzu, `custom` übergibt `--agents` |
| `--effort <stufe>` | Denkaufwand aus Schritt 6; nicht aus `RawResult.json` rekonstruierbar, daher zwingend ins Protokoll |
| `--model <id>` | exakte Modellbindung aus Schritt 4; überschreibt die Modellpräferenz der User-Settings. Volle ID, keine Kurzform. |
Build-Kommandos (`dotnet`, `msbuild`, `nuget`, `npm install`) stehen bewusst auf der Denylist:
Sie würden `bin/`- und `obj/`-Artefakte im Root erzeugen und widersprechen der Methodenvorgabe
„statische Analyse, keine Ausführung".
**Grenzen der Denylist – ehrlich einordnen.** Pattern-Matching auf Shell-Kommandos ist nicht
lückenlos; Umleitungen, Pipes und verschachtelte Aufrufe lassen sich nicht vollständig abfangen.
Die belastbare Read-only-Garantie bleibt der Vorher/Nachher-Vergleich per
`git status --porcelain` aus Abschnitt 1 bzw. 3. Die Denylist senkt das Risiko, sie ersetzt die
Verifikation nicht.
**`--safe-mode` bleibt als zweite Sicherung aktiv.** Die primäre Isolation leistet der
bereinigte Snapshot (Abschnitt 1); `--safe-mode` fängt zusätzlich alles ab, was von außerhalb
des Roots wirken könnte – User-Level-Settings, global installierte Skills, Plugins, Hooks.
Verifiziert am 2026-08-25 gegen Version 2.1.245, dokumentiert als Begründung für den
Snapshot-Ansatz:
- `--safe-mode` verhindert zuverlässig das **Vorladen** von `CLAUDE.md`/`AGENTS.md` als
Systemanweisung. Kontrolltest ohne Werkzeuge: mit `--safe-mode` antwortet der Agent
„NICHTS VORGELADEN", ohne das Flag zitiert er den Inhalt der `CLAUDE.md`.
- `--safe-mode` verhindert **nicht**, dass der Agent solche Dateien als gewöhnliche Dateien
**findet und liest**. Im Smoke-Test mit Shell-Zugriff hat er genau das getan und den Inhalt
von `AGENTS.md` und `CLAUDE.md` wiedergegeben.
Genau deshalb reicht `--safe-mode` allein bei gewährtem Shell-Zugriff nicht aus, und die
KI-Konfigurationen wurden aus dem Snapshot entfernt statt nur deaktiviert.
**Trust-Hinweis auf stderr.** Liegt im Root eine `.claude/settings.json` mit
`permissions.allow`-Einträgen, meldet die CLI auf stderr sinngemäß „Ignoring N permissions.allow
entries … this workspace has not been trusted". Im bereinigten Snapshot tritt das nicht mehr auf;
erscheint die Meldung dennoch, ist der Snapshot nicht sauber – Abschnitt 1 prüfen.
Regeln:
- Arbeitsverzeichnis des Befehls = **Root-Verzeichnis** des Versuchs; geschrieben wird
ausschließlich ins Laufverzeichnis.
- Treten unerwartete Permission-Denials auf, die den Lauf behindern: die Denylist prüfen und die
Abweichung im Protokoll dokumentieren – **nicht** pauschal alle Prüfungen per
`--dangerously-skip-permissions` abschalten. Eine Änderung der Werkzeugkonfiguration ist eine
Änderung der Versuchsbedingung und gehört mit dem User abgestimmt.
- Optional `--max-budget-usd <betrag>` als harte Obergrenze setzen (nur mit `-p` wirksam) und den
Wert im Protokoll notieren. Das Flag arbeitet nur in USD – die **berichtete** Messgröße bleibt
der Tokenverbrauch. Anhaltspunkt: Der bisher aufwendigste Lauf verbrauchte 55.167.397 Tokens.
- Nach Abschluss des Background-Tasks: Endzeit mit `Get-Date -Format o` erfassen.
### 4. Ergebnis auswerten
`RawResult.json` im Laufverzeichnis lesen und defensiv parsen (Feldnamen können je nach Claude-Code-Version
leicht abweichen). Relevante Felder:
| Feld | Bedeutung |
|---|---|
| `is_error`, `subtype` | Lauf erfolgreich oder abgebrochen |
| `duration_ms`, `duration_api_ms` | Gesamt- und reine API-Dauer |
| `num_turns` | Anzahl Agent-Turns (Proxy für Werkzeugaktivität) |
| `total_cost_usd` | Kosten in USD – **wird nicht berichtet**, bleibt aber als Rohwert in der Datei |
| `usage.input_tokens`, `usage.output_tokens` | Tokens **nur des Hauptagenten** |
| `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens` | Cache-Tokens (getrennt ausweisen!) |
| `modelUsage` bzw. `model` | tatsächlich eingesetzte Modell-ID(s), Verbrauch **inklusive aller Subagenten-Ebenen** |
| `permission_denials` | verweigerte Werkzeugaufrufe – Anzahl und Aufschlüsselung nach `tool_name` |
| `subagent_stats` | gestartete Subagenten: `spawned`, `by_type`, `completed`, `failed`, `max_depth` |
| `session_id` | für spätere Transcript-Analyse (`claude --resume <id>`) |
| `result` | Abschlusstext des Agenten |
**`modelUsage` erfasst alle Verschachtelungsebenen – empirisch verifiziert.** Kontrolltest am
2026-08-25 gegen CLI 2.1.245: Eine erzwungene Kaskade (`max_depth: 2`, `spawned: 2`,
`spawned_by_subagents: 1`), bei der ausschließlich der **Enkel-Agent auf Tiefe 2** arbeitete –
er las 15 große Quelldateien, der mittlere Agent reichte nur durch, der Hauptagent las nichts:
| | Tokens |
|---|---:|
| `usage` (nur Hauptagent, 1 Turn) | 49.103 |
| `modelUsage` (gesamt) | **1.689.288** |
| Differenz = Subagenten | 1.640.185 (Faktor 34,4) |
Da die gesamte Leseleistung auf Tiefe 2 stattfand, belegt die Differenz, dass **auch
Sub-Subagenten in `modelUsage` einfließen**. Für die berichtete Aufwandsgröße „Tokens gesamt"
ist `modelUsage` daher die vollständige und einzig richtige Quelle.
**Subagenten-Transkripte werden nicht separat persistiert.** Im Projektverzeichnis liegt je Lauf
genau **eine** `.jsonl`-Datei, und Subagenten-Nachrichten erscheinen darin nicht als Sidechain.
Ihr Verbrauch ist deshalb ausschließlich über `modelUsage` sichtbar, nicht über das Transkript –
und die Summe der Transkript-Nachrichten ist **kein** gültiges Verbrauchsmaß (sie zählt Cache-Reads
über Turns hinweg mehrfach).
Zwei Auswertungsfallen:
- **`usage` erfasst ausschließlich den Hauptagenten.** Für den abrechnungsrelevanten
Gesamtverbrauch ist `modelUsage` über alle Modell-IDs zu summieren. In Iteration 01 standen
105.959 Output-Tokens in `usage` gegenüber 249.040 in `modelUsage`.
- **`duration_api_ms` kann die Wanduhrzeit übersteigen**, wenn Subagenten parallel laufen
(Iteration 01: 47:05 API gegenüber 31:31 Wanduhr). Das ist kein Widerspruch, sondern die
Summe nebenläufiger Anfragen – im Protokoll entsprechend einordnen.
**Pflichtprüfung: tatsächlich eingesetzte Modelle.** Nach jedem Lauf die Schlüssel von
`modelUsage` gegen das angeforderte Modell abgleichen. Zulässig sind nur die angeforderte
Modell-ID und `claude-haiku-4-5-*` (interne Hilfsaufrufe, wenige tausend Tokens). Taucht ein
weiteres Modell auf, ist die **Versuchsbedingung verletzt** – der Lauf ist im Protokoll als
solcher zu kennzeichnen und für Modellvergleiche unbrauchbar.
**`--model` steuert nur den Hauptagenten, nicht zwingend die Subagenten.** Belegt am 2026-08-25
(CLI 2.1.245, Lauf `fable5_builtin_ehigh_v3.7.0-15db`): Mit `--model claude-fable-5` und Modus
`builtin` liefen die 13 Subagenten auf **`claude-opus-5[1m]`** – dem Standardmodell aus
`~/.claude/settings.json`. Auf sie entfielen 136,7 von 150,3 Mio. Tokens (91 %).
| Kombination | Läufe | Modelle in `modelUsage` |
|---|---:|---|
| Sonnet / Opus / Fable + `solo` | 12 | nur das angeforderte Modell (+ Haiku) |
| Sonnet + `builtin` | 11 | nur `claude-sonnet-5` (+ Haiku) |
| **Fable + `builtin`** | **1** | **Fable + `claude-opus-5[1m]`** |
Sonnet und Opus werden an die Subagenten durchgereicht, Fable nicht; Claude Code fällt dann auf
die Sitzungsvorgabe zurück. **`--safe-mode` verhindert das nicht** – es deaktiviert
Customizations, nicht die Modellwahl. Im Modus `solo` tritt das Problem nicht auf, weil es keine
Subagenten gibt.
Bei Modus `builtin` oder `custom` daher **nie** den Flag-Wert allein ins Protokoll schreiben,
sondern die Modelle aus `modelUsage` mit ihrem jeweiligen Anteil ausweisen.
`permission_denials` ist eine **reguläre Messgröße** und immer auszuweisen, auch bei 0. Ein
hoher Wert bedeutet, dass die Werkzeugkonfiguration den Lauf eingeschränkt hat, und ist bei der
Interpretation der Ergebnisqualität zu berücksichtigen.
**Subagenten-Prompts sichern** (entfällt nur im Modus `solo`, dort gibt es keine). Die
vollständigen Prompts, mit denen der Hauptagent seine Subagenten beauftragt hat, stehen im
persistierten Session-Transkript unter
`%USERPROFILE%\.claude\projects\<projekt>\<session_id>.jsonl`. Das mitgelieferte Skript
extrahiert sie:
```powershell
python "<skillverzeichnis>\extract-subagenten.py" "<laufverzeichnis>"
```
Es schreibt `_meta\subagenten.md` (lesbar, mit vollständigem Prompt je Subagent) und
`_meta\subagenten.json` (maschinenlesbar). Erfasst werden Werkzeug (`Task`/`Agent`),
`subagent_type`, `description`, Hintergrund-Flag, Prompt-Länge und Länge des zurückgelieferten
Ergebnisses.
**Plausibilitätskontrolle:** Das Skript vergleicht die gefundene Anzahl mit
`subagent_stats.spawned` minus `spawned_by_subagents`. Im Haupttranskript stehen nämlich nur die
**direkt** vom Hauptagenten gestarteten Subagenten – von Subagenten gestartete (`max_depth` > 1)
liegen in deren eigenen Transkripten. Bei Abweichung setzt das Skript eine Warnung in
`subagenten.md`; dann ist die Ursache zu klären, bevor die Prompts ausgewertet werden.
**Warum das erhoben wird:** In V1b bestimmt der Agent selbst, wie er die Analyse zerlegt – über
alle bisherigen Läufe schwankte das zwischen 0 und 14 Subagenten mit völlig unterschiedlichen
Zuschnitten. Diese selbstgewählte Zerlegung ist die dominierende Störgröße der Versuchsreihe.
Erst die protokollierten Prompts machen sie als Datum auswertbar statt nur als Zahl sichtbar.
Für V2 (`custom`) sind sie zusätzlich der Vergleichsmaßstab: Sie zeigen, wie der Agent ohne
Vorgabe delegiert – gegenüber den vorformulierten Agenten aus `--agents`.
**Anforderungen auswerten** – der inhaltliche Ertrag des Laufs, nicht nur sein Aufwand:
```powershell
python "<skillverzeichnis>nalyse-anforderungen.py" "<laufverzeichnis>"
```
Das Skript parst das im Prompt vorgegebene Blockformat (`ID:`, `Typ:`, `Belege:`, `Status:` …)
aus `Ergebnisse\StRS.md`, `SyRS.md` und `SwRS.md` und schreibt `_metanforderungen.md`
(fertiger Protokollabschnitt) sowie `_metanforderungen.json` (maschinenlesbar). Erhoben werden:
- **Verteilung** über die drei Ebenen
- **Anforderungstypen** (funktional, Sicherheit, Daten, Schnittstelle …)
- **Belegqualität**: Anzahl der Belege, Aufteilung `PRIMÄR`/`SEKUNDÄR`/`KONTEXT`, Median je
Anforderung, Anteil mit mindestens einem Primärbeleg
- **Status**: belegt, `[HYPOTHESE]`, Workaround, Konsolidierungskandidaten
- **Regelkonformität** – geprüft wird gegen die Vorgaben des Prompts selbst:
Belegpflicht (jede Anforderung ≥ 1 Beleg), risikobasierte Priorisierung (Sicherheit,
Abrechnung, Berechtigungen brauchen `PRIMÄR` **oder** `[HYPOTHESE]`), Verifizierbarkeit
(Prüfidee vorhanden), Traceability (Tracelinks gesetzt)
**Zuordnung zum Evaluationsrahmen (Kap. 4.3).** Die erhobenen Kenngrößen decken die
**maschinell prüfbare** Hälfte der drei Qualitätsdimensionen ab; die Expertenbewertung nach
Likert-Skala bleibt davon unberührt und tritt daneben:
| Dimension (Kap. 4.3) | Maschinell erhoben | Bleibt der Expertenbewertung |
|---|---|---|
| **Statement-Qualität** | Belegqualität, Primärbelegquote, Anteil mit Prüfidee, Hypothesenanteil, Übernahmewürdigkeit | Eindeutigkeit der Formulierung, sachliche Korrektheit, Verständlichkeit |
| **Set-Qualität** | Verteilung über die Ebenen, Konsolidierungskandidaten, doppelte IDs, Anforderungen ohne Beleg | Vollständigkeit der Prozessabdeckung, Redundanzfreiheit im fachlichen Sinn |
| **Traceability-Qualität** | Anteil mit Tracelinks, Belegklassifikation, Tracelinks auf nicht existierende IDs | Auffindbarkeit des Belegs, Nachvollziehbarkeit der Ableitung |
**Warum das erhoben wird:** Die Anforderungsanzahl allein ist als Qualitätsmaß wertlos – sie
streute über Läufe gleicher Bedingung um Faktor 5,9. Belegdichte, Primärbelegquote und
Regelverstöße sind dagegen inhaltliche Größen und machen Läufe vergleichbar, deren Mengengerüst
weit auseinanderliegt. Die Regelkonformitätsprüfung deckt zudem auf, wo der Agent die eigenen
Vorgaben des Prompts verfehlt hat – ein Befund, der sich sonst nur durch Handlektüre ergäbe.
Der erzeugte Abschnitt wird **unverändert** als `## Gefundene Anforderungen` ins Protokoll
übernommen, zwischen `## Ergebnis` und den Vergleichs- bzw. Anmerkungsabschnitten.
Erzeugte Dateien: Inhalt von `<laufverzeichnis>\Ergebnisse\` auflisten. Zusätzlich prüfen,
ob das Root unverändert blieb: `git -C <root> status --porcelain` gegen `before.txt`
vergleichen (Nachher-Stand nach `$lauf\_metafter.txt`) – Abweichungen als Auffälligkeit
ins Protokoll. Beim Vergleich Zeilenenden
normalisieren (`before.txt` entsteht je nach Werkzeug mit CRLF, der Nachher-Stand mit LF),
sonst meldet ein naiver `diff` alle Zeilen als geändert.
### 5. Messprotokoll schreiben
Als `Protokoll.md` ins Laufverzeichnis, neben `RawResult.json` und `Stderr.log`.
Vorlage:
```markdown
# Messprotokoll – <Versuch> – Iteration <NN>
## Lauf
- **Prompt-Datei:** <relativer Pfad>
- **SHA-256 (Prompt):** <hash>
- **Startzeit:** <ISO 8601>
- **Endzeit:** <ISO 8601>
- **Dauer gesamt:** <hh:mm:ss> (API: <hh:mm:ss>)
- **Root-Verzeichnis:** <Pfad>
- **Codebasis-Commit:** <hash> (dirty: ja/nein)
- **Snapshot-Zustand:** <bereinigt von KI-Konfigurationen: ja/nein; Remote entkoppelt: ja/nein>
- **Prompt-Repo-Commit:** <hash>
## Werkzeugkonfiguration
- **Skill-Version:** <version aus dem Frontmatter dieses Skills>
- **Claude-Code-Version:** <version>
- **CLI-Pfad:** <aufgelöster Pfad zur claude.exe>
- **Modell (angefordert):** <Wert von `--model`>
- **Modelle (tatsächlich eingesetzt):** <alle Schlüssel aus `modelUsage` mit Tokenanteil>
- **Kontrolle Modell:** <bestanden | **verletzt**: nicht angefordertes Modell <ID> mit <N> Tokens>
- **Effort:** <low | medium | high | xhigh | max> (per `--effort` gesetzt; Gegenprobe im
Transkript-Feld `effort`)
- **Laufverzeichnis-ID:** `v<skillversion>-<id4>` aus dem Verzeichnisnamen
- **Ablage:** `<ModellID>/<Agentenmodus>/<Effort>/`
- **Parallele Läufe:** <nein | ja: welche Laufverzeichnisse liefen zeitgleich>
- **Agentenmodus:** <solo (V1) | builtin (V1b) | custom (V2)>; bei `custom` zusätzlich
Pfad und SHA-256 der Agentendefinitionen
- **Kontextfenster:** <Tokens, z. B. aus `modelUsage.contextWindow`>
- **Sampling-Parameter:** <Temperatur u. a. | nicht steuerbar>
- **Nur bei lokalem Modellbetrieb:** Inferenz-Runtime samt Version, Quantisierungsstufe des
Modell-Builds
- **Permission-Mode:** <acceptEdits | ...>
- **Toolfreigabe:** `--allowedTools <wörtlich>` / `--disallowedTools <wörtlich>`
- **Isolationsmechanismus:** <--safe-mode, --strict-mcp-config, ggf. --setting-sources>
- **MCP-Server / Agentendateien:** <keine – aus dem Snapshot entfernt, zusätzlich --safe-mode | Liste>
- **Subagenten:** <Anzahl und Typ aus `subagent_stats`, z. B. 8 × Explore, 0 fehlgeschlagen>
- **Verschachtelung:** `spawned` = <N>, davon `spawned_by_subagents` = <M>, `max_depth` = <D>.
Bei `max_depth` > 1 ausdrücklich vermerken – die Tokens der tieferen Ebenen sind in
„Tokens gesamt" enthalten, ihre Prompts jedoch **nicht** in `_meta\subagenten.md`
## Validierungsstichprobe
- **Größe:** <vor Versuchsbeginn festgelegte Anzahl Anforderungen>
- **Ziehungsverfahren:** <Zufallsstichprobe über alle Ebenen | ...>
- **Validatoren:** <Anzahl und fachlicher Zuschnitt>
- **Stand:** <noch nicht gezogen | gezogen am <Datum> | ausgewertet>
Kapitel 4.3 verlangt, die Stichprobengröße **vor** Versuchsbeginn festzulegen und im
Versuchsprotokoll zu dokumentieren. Ist für den Lauf keine Validierung vorgesehen, hier
`entfällt` eintragen – das Feld darf nicht fehlen.
## Verbrauch
### Hauptagent (`usage`)
| Messgröße | Wert |
|---|---|
| Input-Tokens | |
| Output-Tokens | <Wert> (davon N Thinking-Tokens aus `usage.output_tokens_details`) |
| Cache-Write-Tokens | |
| Cache-Read-Tokens | |
| Agent-Turns | |
### Gesamtlauf inkl. Subagenten (`modelUsage`, abrechnungsrelevant)
| Messgröße | <Modell-ID> | <weitere Modell-ID> | Summe |
|---|---:|---:|---:|
| Input-Tokens | | | |
| Output-Tokens | | | |
| Cache-Write-Tokens | | | |
| Cache-Read-Tokens | | | |
| **Tokens gesamt** | | | |
**Tokens gesamt: <Summe>** — Input + Output + Cache-Write + Cache-Read über alle Modelle.
Das ist die **berichtete Aufwandsgröße** der Versuchsreihe. `total_cost_usd` bleibt unberührt in
`RawResult.json` erhalten, wird aber nicht ins Protokoll übernommen: Token sind modell- und
preisunabhängig und bleiben damit über Preisänderungen und Modellwechsel hinweg vergleichbar.
## Gefundene Anforderungen
<unverändert aus `_metanforderungen.md` übernehmen – Verteilung, Typen, Belegqualität,
Status, Regelkonformität>
## Ergebnis
- **Status:** <erfolgreich | Fehler: ...>
- **Session-ID:** <id>
- **Permission-Denials:** <Anzahl> (<Aufschlüsselung nach Tool>; bei keinem Denial: 0).
Im Modus `solo` Denials auf `Task`/`Agent` **getrennt** ausweisen – sie sind die
Versuchsbedingung, keine Einschränkung.
- **Kontrolle Agentenmodus:** `subagent_stats.spawned` = <Zahl> (bei `solo` muss 0 stehen,
sonst Fehlmessung)
- **Subagenten-Prompts:** <`_meta\subagenten.md`, N Aufrufe erfasst | entfällt (Modus solo)>
- **Erzeugte Dateien:** <Liste aus Ergebnisse\>
- **Root unverändert:** <ja | nein: welche Abweichungen>
- **Abschlusstext des Agenten:** siehe RawResult.json (`result`)
- **Anmerkungen/Auffälligkeiten:** <Beobachtungen, Abbrüche, manuelle Eingriffe>
```
### 6. Abschlussbericht an den User
Kurz zusammenfassen: Status, Dauer, Tokenverbrauch (inkl. Cache), Modell, Pfad des
Laufverzeichnisses und welche Dateien der Lauf erzeugt hat. Bei `is_error` oder leerem
Ergebnis: `Stderr.log` und `RawResult.json` zitieren, nicht spekulieren.
## Werkzeugadapter
Der Prozess oben ist werkzeugneutral. Dieser Abschnitt hält fest, wie er mit einem konkreten
Werkzeug umgesetzt wird und welche Messgrößen dieses Werkzeug liefert.
### Adapter: Claude Code
Der einzige derzeit ausgearbeitete Adapter. Alle konkreten Aufrufe, Flags und Feldnamen im
Prozessteil beziehen sich auf ihn; er wurde gegen **CLI 2.1.245** entwickelt und verifiziert.
**Gelieferte Messgrößen** – vollständig aus `RawResult.json`:
| Messgröße | Feld |
|---|---|
| Abbruchstatus | `is_error`, `subtype`, `stop_reason`, `terminal_reason` |
| Dauer | `duration_ms`, `duration_api_ms` (Wanduhr wird selbst gemessen) |
| Tokens Hauptagent | `usage.*` inkl. `output_tokens_details.thinking_tokens` |
| Tokens gesamt inkl. aller Subagenten-Ebenen | `modelUsage` je Modell-ID |
| Tatsächlich eingesetzte Modelle | Schlüssel von `modelUsage` |
| Verweigerte Werkzeugaufrufe | `permission_denials` |
| Subagenten | `subagent_stats` inkl. `spawned_by_subagents`, `max_depth`, `failed` |
| Sitzungskennung für Transcript-Analyse | `session_id` |
**Nicht erfasst:** Sampling-Parameter wie Temperatur sind über die CLI nicht steuerbar und
werden nicht ausgewiesen; der Effort steht **nicht** in `RawResult.json` und ist nur über das
Session-Transkript belegbar (siehe Schritt 6 der Vorbereitung).
### Weitere Adapter (für Versuch 4 nachzurüsten)
Kapitel 4 der Arbeit sieht einen LLM-Querschnitt über Codex CLI, Qwen Code CLI über LM Studio
und DeepSeek über die Cloud-API vor. Diese Adapter sind noch nicht ausgearbeitet. Damit ein
Lauf als Messpunkt taugt, muss ein Adapter mindestens liefern:
| Pflichtangabe | Zweck |
|---|---|
| Modell-ID und Kontextfenster | Bedingung dokumentieren |
| Input- und Output-Tokens | Aufwandsvergleich |
| Dauer | Aufwandsvergleich |
| Abbruchstatus | Gültigkeit des Laufs |
| Erzeugte Artefakte | Ertrag |
| Sampling-Parameter, soweit steuerbar | Reproduzierbarkeit (Kap. 4.3) |
| Bei lokalem Betrieb: Runtime samt Version, Quantisierungsstufe | Reproduzierbarkeit (Kap. 4.3) |
Was ein Adapter nicht liefern kann, wird im Protokoll ausdrücklich als `nicht erfasst`
ausgewiesen – niemals geschätzt. Fehlen mehrere Pflichtangaben, ist der Lauf nur eingeschränkt
mit Claude-Code-Läufen vergleichbar; das gehört in die Anmerkungen des Protokolls.
## Randbedingungen
- Niemals Messwerte schätzen oder erfinden – fehlt ein Feld im JSON, im Protokoll als
`nicht erfasst` eintragen.
- Prompt-Datei unverändert lassen; das Protokoll ist eine neue Datei.
- Läuft der Background-Task ungewöhnlich lange, User informieren statt abbrechen.
- **Am Codebasis-Root wird nichts verändert** – weder zur Vorbereitung noch zur Isolation noch
zur Nachbereitung. Ist das Root vor dem Lauf bereits `dirty`, den Zustand im Protokoll
festhalten und den User informieren, statt ihn stillschweigend zu bereinigen.
- **Der Codebasis-Snapshot ist eingefroren.** Kein `push`, kein `fetch`, kein Hinzufügen eines
Remotes, kein Commit im Codebasis-Repo. Soll der Snapshot geändert werden (etwa weil eine
weitere KI-Konfiguration auftaucht), ist das mit dem User abzustimmen, als eigener Commit
festzuschreiben und in allen betroffenen Protokollen als geänderte Versuchsbedingung zu
vermerken.
- **Werkzeugkonfiguration ist eine unabhängige Variable.** Weicht ein Lauf von der hier
festgelegten Standardkonfiguration ab (andere Flags, anderer Permission-Mode, zusätzliche
Tools), ist das im Protokoll unter „Werkzeugkonfiguration" wörtlich zu dokumentieren –
sonst sind die Läufe nicht vergleichbar.
## Versionierung
Die aktuelle Version steht im Frontmatter (`version:`) und **muss in jedes Protokoll** unter
„Werkzeugkonfiguration" als **Skill-Version** eingetragen werden. Nur so lässt sich später
zuordnen, unter welcher Fassung des Versuchsaufbaus eine Messung entstanden ist.
Schema (Semantic Versioning):
| Stelle | Bedeutung | Folge für die Vergleichbarkeit |
|---|---|---|
| **MAJOR** | Änderung der Versuchsbedingung – Flags, Toolfreigabe, Isolationsmechanismus, Codebasis-Snapshot | Läufe verschiedener MAJOR-Versionen sind **nicht** direkt vergleichbar |
| **MINOR** | Neue Pflichtschritte oder Messgrößen ohne Änderung der Versuchsbedingung | Läufe bleiben vergleichbar; die Protokolle werden reichhaltiger |
| **PATCH** | Fehlerkorrekturen am Ablauf, Dokumentation, Klarstellungen | Ohne Einfluss auf die Messung |
**Regel:** Wer den Skill ändert, erhöht die Version im Frontmatter und ergänzt eine Zeile in
der Historie unten – im selben Arbeitsschritt.
## Änderungshistorie
| Version | Änderung | Grund | Verwendet in |
|---|---|---|---|
| **1.0.0** | Ausgangsfassung: `--permission-mode acceptEdits`, kein Shell-Zugriff; Isolation durch Löschen der KI-Konfigurationsdateien im Root vor dem Lauf und `git restore` danach | Erstaufsetzung des Versuchsaufbaus | Lauf 1 (`01_Lauf_2026-08-25_1228`) |
| **2.0.0** | `--allowedTools "Bash" "PowerShell"` + 33er-Denylist für schreibende und bauende Kommandos. Isolation über **eingefrorenen Codebasis-Snapshot ohne KI-Konfigurationen** (Commit `79c1142`, Parent `89ccfd6`, GitHub-Remote entkoppelt), zusätzlich `--safe-mode` und `--strict-mcp-config`. Der destruktive Lösch-/Restore-Schritt pro Lauf entfällt. `permission_denials` und `subagent_stats` werden reguläre Messgrößen; Verbrauchstabelle trennt Hauptagent und Gesamtlauf. CLI-Pfad-Auflösung als eigener Schritt. | Lauf 1 erzeugte 36 Permission-Denials (32 × Bash, 4 × PowerShell); Shell-gestützte Verzeichnisinventuren fehlten dem Agenten. `--safe-mode` allein genügt nicht: Es unterdrückt das Vorladen von `CLAUDE.md`/`AGENTS.md`, verhindert aber nicht, dass der Agent sie mit Shell-Zugriff selbst liest – im Smoke-Test nachgewiesen. | Lauf 2 (`01_Lauf_2026-08-25_1349`, API-Abbruch) |
| **2.0.1** | `before.txt` wird leerwertsicher geschrieben (`Set-Content -Value (… \| Out-String)` statt Pipeline) | Bei sauberem Root schreibt `Set-Content` aus leerer Pipeline die Datei nicht und lässt alten Inhalt stehen; der Vorher/Nachher-Vergleich meldete dadurch eine Abweichung, die es nicht gab | Lauf 3 (`01_Lauf_2026-08-25_1429`) |
| **2.1.0** | **Modell wird vor jedem Lauf beim User erfragt** (Pflichtparameter, kein Default); Kurzformen `opus`/`sonnet`/`fable` untersagt | Das Modell ist eine unabhängige Variable und wurde bis dahin ad hoc gewählt. Kurzformen lösen auf das jeweils neueste Modell auf und verschieben die Versuchsbedingung über die Zeit. | Lauf 4 (`01_Lauf_2026-08-25_1505`) |
| **2.1.1** | Warnung zur Snapshot-Prüfung: Regex nicht vereinfachen, `Select-String` ist case-insensitiv; Beispiel-Falschtreffer dokumentiert | Ein verkürztes Muster sprach auf echte Produktdateien an (`ClaudeCodeChatModelClient.cs`, `GoogleGeminiChatModelClient.cs` – KI-Assistenz-Feature der ERP-Suite) und hätte beinahe einen Fehlabbruch ausgelöst | – |
| **3.0.0** | **Agentenmodus als Pflichtparameter** (`solo` / `builtin` / `custom`), vor jedem Lauf beim User erfragt. `solo` sperrt `Task`/`Agent`, `custom` übergibt vordefinierte Agenten per `--agents`. Neue Protokollfelder: Agentenmodus und Kontrolle `subagent_stats.spawned`. | Der Subagenten-Einsatz schwankte bei identischem Prompt zwischen 0 und 14 und war damit die dominierende Störgröße: Drei Läufe unter sonst identischer Bedingung ergaben 55 / 106 / 325 Anforderungen und 27,6 / 11,5 / 52,7 Mio. Tokens (Faktor 5,9 bzw. 4,6). Ohne Festlegung des Modus ist keine Bedingung sauber messbar. | ab Versuch 1 neu |
| **3.1.0** | **Parallelfähigkeit**: Laufverzeichnis sekundengenau plus Modus und 4-stellige Zufalls-ID; alle Steuerdateien nach `_meta\` im Laufverzeichnis statt in den gemeinsamen Scratchpad; neue Protokollfelder „Laufverzeichnis-ID" und „Parallele Läufe"; Abschnitt zu den messtechnischen Grenzen des Parallelbetriebs | Minutengenaue Namen kollidieren bei zwei Starts in derselben Minute, und geteilte Scratchpad-Dateien (`before.txt`, `combined_prompt.md`, Zeitstempel) hätten sich zwischen parallelen Läufen gegenseitig überschrieben. Nebeneffekt: Der exakt gesendete Prompt ist nun je Lauf archiviert. | – |
| **3.2.0** | Verzeichnisname enthält zusätzlich **Modell-Kurzform und Skill-Version**: `<NN>_Lauf_<yyyy-MM-dd_HHmmss>_<modell>_<modus>_v<skillversion>-<id4>`. Solo-Modus sperrt zusätzlich **`Workflow`**. | Modell, Modus und Skill-Version sind die unabhängigen Variablen der Reihe und waren bisher nur im Protokoll sichtbar – im Namen verhindern sie, dass Läufe verschiedener Bedingungen gemeinsam ausgewertet werden. Die `Workflow`-Sperre schließt eine im Verifikationstest entdeckte Lücke: Nach der Sperre von `Task`/`Agent` versuchte der Agent, über `Workflow` zu orchestrieren. | ab Lauf 7 |
| **3.3.0** | **Subagenten-Prompts werden protokolliert.** Neues Skript `extract-subagenten.py` zieht aus dem Session-Transkript alle Subagenten-Aufrufe samt vollständigem Prompt nach `_meta\subagenten.md` / `.json`; neues Protokollfeld „Subagenten-Prompts". | Die selbstgewählte Zerlegung der Analyse ist die dominierende Störgröße (0 bis 14 Subagenten bei identischem Prompt) und war bisher nur als Zahl sichtbar. Mit den Prompts wird sie inhaltlich auswertbar und für V2 zum Vergleichsmaßstab gegenüber vorformulierten Agenten. | rückwirkend für alle Läufe angewandt |
| **3.4.0** | **Effort als dritter Pflichtparameter**: `--effort` wird vor jedem Lauf erfragt und im Protokoll geführt; Thinking-Tokens sind in der Verbrauchstabelle vorgesehen. | Der Denkaufwand ist eine unabhängige Variable, wurde aber bis dahin nur aus der Sitzung geerbt und nirgends dokumentiert. `RawResult.json` enthält **kein** `effort`-Feld – nachträglich ist er nur aus dem Session-Transkript belegbar. Rekonstruktion ergab: alle Läufe 1 bis B liefen auf `high`, die Bedingung war also konstant. | ab dem nächsten Lauf |
| **3.5.0** | **Berichtete Aufwandsgröße ist der Tokenverbrauch statt der USD-Kosten.** Verbrauchstabelle und alle Vergleiche führen „Tokens gesamt" (Input + Output + Cache-Write + Cache-Read über alle Modelle); `total_cost_usd` bleibt als Rohwert in `RawResult.json`, wird aber nicht mehr ins Protokoll übernommen. | USD-Beträge hängen an Preisliste und Modellwahl und veralten. Token sind die unmittelbare Verbrauchsgröße, bleiben über Preisänderungen und Modellwechsel hinweg vergleichbar und sind in der Arbeit ohne Währungsbezug zitierbar. | rückwirkend auf alle Protokolle angewandt |
| **3.6.0** | **Verschachtelte Subagenten dokumentiert und verifiziert.** Belegt, dass `modelUsage` auch Sub-Subagenten (Tiefe ≥ 2) erfasst; neues Protokollfeld „Verschachtelung" mit `spawned`, `spawned_by_subagents` und `max_depth`. Festgehalten, dass Subagenten-Transkripte nicht separat persistiert werden. | Bis dahin war unbelegt, ob tiefere Ebenen in die berichtete Tokensumme einfließen. Kontrolltest mit erzwungener Kaskade (Arbeit ausschließlich auf Tiefe 2): 49.103 Tokens im Hauptagenten gegenüber 1.689.288 in `modelUsage` – die Differenz stammt nachweislich vom Enkel-Agenten. | ab sofort |
| **3.7.0** | **Effort im Verzeichnisnamen**: `<NN>_Lauf_<yyyy-MM-dd_HHmmss>_<modell>_<modus>_e<effort>_v<skillversion>-<id4>`, z. B. `_sonnet5_solo_ehigh_v3.7.0-a3f1`. | Das Schema entstand, als der Effort bei allen Läufen konstant `high` war. Mit dem ersten `max`-Block (Läufe P, Q) variiert er – gleichnamige Verzeichnisse hätten unterschiedliche Bedingungen bezeichnet und damit den Zweck des Schemas verfehlt. Der Effort ist zudem als möglicherweise stärkste Einflussgröße aufgefallen: Der `max`-Block erreichte die höchsten Thinking-Token-Werte der gesamten Reihe. | rückwirkend auf alle Laufverzeichnisse angewandt |
| **3.8.0** | **Pflichtprüfung der tatsächlich eingesetzten Modelle.** `modelUsage` ist nach jedem Lauf gegen das angeforderte Modell abzugleichen; neue Protokollfelder „Modell (angefordert)", „Modelle (tatsächlich eingesetzt)" und „Kontrolle Modell". | `--model` steuert nur den Hauptagenten. Im Lauf `fable5_builtin_ehigh_v3.7.0-15db` liefen die 13 Subagenten auf `claude-opus-5[1m]` statt auf Fable – 91 % des Verbrauchs entfielen auf ein nicht angefordertes Modell, und der Lauf wurde mit 150,3 Mio. Tokens der teuerste der Reihe. Ohne diese Prüfung bleibt die Bedingungsverletzung unbemerkt. | ab sofort; Gegenprüfung der 23 Vorläufe ergab keine weiteren Fälle |
| **3.9.0** | **Pflichtabschnitt „Gefundene Anforderungen" im Protokoll.** Neues Skript `analyse-anforderungen.py` wertet die erzeugten Anforderungen aus: Verteilung, Typen, Belegqualität (`PRIMÄR`/`SEKUNDÄR`/`KONTEXT`), Status, Konsolidierungskandidaten und **Regelkonformität gegen die Vorgaben des Prompts**. | Die Protokolle maßen bis dahin nur den Aufwand, nicht den Ertrag. Die reine Anforderungsanzahl taugt nicht als Qualitätsmaß (Streuung Faktor 5,9 bei gleicher Bedingung); Belegdichte und Regelverstöße sind inhaltliche Größen. Erste Anwendung deckte sofort Verstöße gegen die risikobasierte Priorisierung auf. | rückwirkend auf alle 24 Protokolle angewandt |
| **3.10.0** | **Bedingungen als Ordnerstruktur statt im Dateinamen.** Ablage unter `<ModellID>\<Agentenmodus>\<Effort>\`; der Verzeichnisname lautet wieder `<NN>_Lauf_<yyyy-MM-dd_HHmmss>_v<skillversion>-<id4>`. Neues Protokollfeld „Ablage". | Der Name wuchs mit jeder unabhängigen Variable und war mit fünf Bestandteilen kaum lesbar. Als Ordnerebenen sind die Bedingungen navigierbar, je Zelle unmittelbar abzählbar und um weitere Variablen erweiterbar, ohne bestehende Namen zu brechen. | rückwirkend auf alle 24 Läufe angewandt |
| **4.0.0** | **Ausrichtung auf Kapitel 4 der Arbeit.** Skill zweigeteilt in `## Prozess` (werkzeugneutral) und `## Werkzeugadapter` (konkrete Aufrufe, derzeit nur Claude Code). Der Prompt trägt nur noch die Analyseanweisung; Werkzeugkontext und Ausgabeverzeichnis werden zur Laufzeit angehängt. Versuchszuordnung korrigiert: **V1 = `solo`, V1b = `builtin`**. Neue Protokollfelder: Kontextfenster, Sampling-Parameter, lokale Runtime-Angaben, Abschnitt Validierungsstichprobe. Abschnitt „Gefundene Anforderungen" den drei Qualitätsdimensionen zugeordnet. | Prompt und Skill waren über 24 Läufe organisch gewachsen und stellenweise nicht mehr deckungsgleich mit Kap. 4. Der Prompt enthielt Aussagen zur Werkzeugkonfiguration, die dort nicht hingehören und ihn an Claude Code banden; der Skill verankerte weder den Evaluationsrahmen noch die von Kap. 4.3 geforderten Reproduzierbarkeitsangaben. MAJOR, weil sich die Zuordnung der Versuchsbedingungen ändert. | ab sofort |
## Parallele Läufe
Mehrere Läufe dürfen gleichzeitig gestartet werden. Voraussetzung ist, dass **kein Zustand
geteilt** wird:
- **Verzeichnisname** ist sekundengenau und trägt Modus plus 4-stellige Zufalls-ID – zwei
gleichzeitige Starts können nicht kollidieren (Schritt 7 würfelt bei Kollision neu).
- **Alle Steuerdateien** (`combined_prompt.md`, `before.txt`, `after.txt`, Zeitstempel) liegen
in `_meta\` **im jeweiligen Laufverzeichnis**. Der gemeinsame Scratchpad wird für Laufdaten
**nicht** verwendet – dort würden parallele Läufe einander überschreiben.
- **Der Codebasis-Snapshot wird nur gelesen.** Beliebig viele Läufe dürfen ihn gleichzeitig
lesen. Die Vorher/Nachher-Prüfung bleibt gültig, weil kein Lauf schreibt.
- **Endeerkennung je Lauf** über das Erscheinen der eigenen `RawResult.json`, nicht über einen
gemeinsamen Marker.
**Messtechnische Einschränkung – im Protokoll zwingend vermerken.** Gleichzeitig laufende
Versuche konkurrieren um CPU, Netzwerk und API-Kontingent. Betroffen sind:
| Messgröße | Bei Parallelbetrieb |
|---|---|
| Wanduhrzeit | **verzerrt** – nicht mit seriellen Läufen vergleichbar |
| `duration_ms`, `duration_api_ms` | **verzerrt** |
| Tokenverbrauch, Anforderungsanzahl, Denials | unverzerrt |
Für Laufzeitvergleiche daher **seriell** messen. Parallelbetrieb eignet sich, um schnell
mehrere Datenpunkte für Umfang, Tokenverbrauch und Varianz zu sammeln. Das Feld
**„Parallele Läufe"** im Protokoll nennt die zeitgleich laufenden Laufverzeichnisse; steht dort
etwas anderes als `nein`, sind die Zeitangaben des Laufs nicht für Laufzeitvergleiche zu
verwenden.
## Zuordnung Versuch ↔ Agentenmodus
Nach Kapitel 4 der Arbeit (Versuchsdesign):
| Versuch | Modus | Bedeutung |
|---|---|---|
| **V1** – Baseline (Prompt-only) | `solo` | Ein Thread, ein Kontext; keine Subagenten |
| **V1b** – Baseline mit werkzeugeigenen Agenten | `builtin` | Eingebaute Subagenten des Werkzeugs zugelassen |
| **V2** – Agentengestützt | `custom` | Rollenspezialisierte Agentendateien: Stakeholder, System, Software, ISO-29148-Orchestrator |
| **V3** – Werkzeugzugriff | `custom` + MCP | Wie V2, zusätzlich externe Werkzeugserver |
**Abgrenzung V1 gegen V1b.** Die Arbeit unterscheidet zwischen *Agentendateien* (V2) und den
*werkzeugeigenen* Subagenten, die eine CLI von sich aus mitbringt. Letztere sind keine
Konfigurationsartefakte und damit nach dem Wortlaut von V1 nicht ausgeschlossen – ihr Einsatz
verändert die Ergebnisse jedoch erheblich. V1b trennt beide Fälle, sodass der Effekt der
werkzeugeigenen Delegation getrennt vom Effekt der rollenspezialisierten Agentendateien
messbar wird.
**Bisherige Läufe.** Die 24 Läufe in `Versuche/Versuch_01/` verteilen sich auf V1 (`solo`) und
V1b (`builtin`). Die Ordnerstruktur macht die Zuordnung unmittelbar sichtbar; die Zellen sind
über `ls <ModellID>/<Modus>/<Effort> | wc -l` abzählbar.
@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
"""Wertet die erzeugten Anforderungen eines Laufs aus und schreibt einen Protokollabschnitt.
Aufruf: python analyse-anforderungen.py <laufverzeichnis> [--print]
Erzeugt `<laufverzeichnis>/_meta/anforderungen.md` (der fertige Abschnitt) und
`<laufverzeichnis>/_meta/anforderungen.json` (maschinenlesbar).
Geparst wird das im Prompt vorgegebene Blockformat (`ID:`, `Typ:`, `Belege:`, `Status:` ...).
"""
import io, json, os, re, sys, collections
EBENEN = ('StRS', 'SyRS', 'SwRS')
# Anforderungen, fuer die der Prompt mindestens einen PRIMAER-Beleg verlangt
RISIKO = re.compile(r'sicherheit|abrechnung|fakturier|berechtigung|recht|zugriff|authentifiz|'
r'passwort|rolle|lizenz|steuer|zahlung|mahn', re.I)
def bloecke(pfad):
"""Zerlegt eine Anforderungsdatei in Bloecke ab jeder ID:-Zeile."""
if not os.path.exists(pfad):
return
text = io.open(pfad, encoding='utf-8').read()
teile = re.split(r'(?m)^ID:', text)
for t in teile[1:]:
yield 'ID:' + t
def feld(block, name):
m = re.search(r'(?m)^%s:[ \t]*(.*)$' % re.escape(name), block)
return m.group(1).strip() if m else ''
def analysiere(lauf):
erg = os.path.join(lauf, 'Ergebnisse')
anf = []
for eb in EBENEN:
for b in bloecke(os.path.join(erg, eb + '.md')):
aid = feld(b, 'ID')
if not aid:
continue
belege = re.findall(r'\[(PRIM\w*R|SEKUND\w*R|KONTEXT)\]', b)
norm = []
for x in belege:
norm.append('PRIMÄR' if x.startswith('PRIM')
else 'SEKUNDÄR' if x.startswith('SEKUND') else 'KONTEXT')
status = feld(b, 'Status')
anf.append(dict(
id=aid, ebene=eb, titel=feld(b, 'Titel'), typ=feld(b, 'Typ') or '(ohne)',
belege=norm, status=status,
hypothese=('HYPOTHESE' in status.upper()) or ('[HYPOTHESE]' in b),
workaround='workaround' in status.lower(),
tracelinks=feld(b, 'Tracelinks'),
konsolidierung=feld(b, 'Konsolidierung'),
pruefidee=feld(b, 'Prüfidee') or feld(b, 'Pruefidee'),
qm=feld(b, 'Qualitätsmerkmal'),
uebernahme=feld(b, 'Übernahmewürdigkeit') or feld(b, 'Uebernahmewuerdigkeit'),
))
return anf
def de(n):
return '{:,}'.format(int(n)).replace(',', '.')
def pct(a, b):
if not b:
return '–'
return ('%.1f' % (100.0 * a / b)).replace('.', ',') + ' %'
def abschnitt(anf):
n = len(anf)
if not n:
return '## Gefundene Anforderungen\n\nKeine Anforderungen im vorgegebenen Format gefunden.\n'
je_ebene = collections.Counter(a['ebene'] for a in anf)
typen = collections.Counter(a['typ'] for a in anf)
bel = collections.Counter()
for a in anf:
bel.update(a['belege'])
bel_ges = sum(bel.values())
ohne_beleg = [a for a in anf if not a['belege']]
hyp = [a for a in anf if a['hypothese']]
work = [a for a in anf if a['workaround']]
ohne_trace = [a for a in anf if not a['tracelinks'] or a['tracelinks'].lower() in ('-', '–', 'keine')]
ohne_pruef = [a for a in anf if not a['pruefidee']]
kons = [a for a in anf if a['konsolidierung'] and a['konsolidierung'].lower() != 'nein']
mit_qm = [a for a in anf if a['qm']]
# Uebernahmewuerdigkeit: erst ab Prompt-Fassung 2026-08-26; aeltere Laeufe kennen das Feld nicht
uebern = collections.Counter()
for a in anf:
v = a['uebernahme'].lower()
if not v:
continue
for schl in ('übernehmen', 'workaround', 'sonderfall', 'veraltet'):
if schl in v:
uebern[schl] += 1
break
else:
uebern['(sonstige Angabe)'] += 1
mit_uebern = sum(uebern.values())
risiko = [a for a in anf if RISIKO.search(a['typ'] + ' ' + a['titel'])]
risiko_ungedeckt = [a for a in risiko if 'PRIMÄR' not in a['belege'] and not a['hypothese']]
zahlen = sorted(len(a['belege']) for a in anf)
median = zahlen[n // 2] if n % 2 else (zahlen[n // 2 - 1] + zahlen[n // 2]) / 2.0
z = ['## Gefundene Anforderungen', '',
'Maschinell aus `Ergebnisse\\StRS.md`, `SyRS.md` und `SwRS.md` ausgewertet '
'(Blockformat des Prompts). Erzeugt von `analyse-anforderungen.py`.', '',
'Die Kenngrößen decken die **maschinell prüfbare** Hälfte des Evaluationsrahmens aus '
'Kapitel 4.3 ab: Belegqualität und Übernahmewürdigkeit gehören zur *Statement-Qualität*, '
'Verteilung und Konsolidierungskandidaten zur *Set-Qualität*, Tracelinks und '
'Belegklassifikation zur *Traceability-Qualität*. Die Expertenbewertung nach '
'Likert-Skala tritt daneben und wird hier nicht ersetzt.', '',
'### Verteilung über die Ebenen', '',
'| Ebene | Anzahl | Anteil |', '|---|---:|---:|']
for eb in EBENEN:
z.append('| %s | %d | %s |' % (eb, je_ebene.get(eb, 0), pct(je_ebene.get(eb, 0), n)))
z += ['| **Gesamt** | **%d** | 100 %% |' % n, '']
z += ['### Anforderungstypen', '', '| Typ | Anzahl | Anteil |', '|---|---:|---:|']
for t, c in typen.most_common(10):
z.append('| %s | %d | %s |' % (t, c, pct(c, n)))
if len(typen) > 10:
rest = sum(c for _, c in typen.most_common()[10:])
z.append('| (%d weitere) | %d | %s |' % (len(typen) - 10, rest, pct(rest, n)))
z.append('')
z += ['### Belegqualität', '', '| Messgröße | Wert |', '|---|---:|',
'| Belege gesamt | %s |' % de(bel_ges),
'| davon `PRIMÄR` | %s (%s) |' % (de(bel['PRIMÄR']), pct(bel['PRIMÄR'], bel_ges)),
'| davon `SEKUNDÄR` | %s (%s) |' % (de(bel['SEKUNDÄR']), pct(bel['SEKUNDÄR'], bel_ges)),
'| davon `KONTEXT` | %s (%s) |' % (de(bel['KONTEXT']), pct(bel['KONTEXT'], bel_ges)),
'| Belege je Anforderung (Median) | %s |' % str(median).replace('.', ','),
'| Anforderungen mit mindestens einem `PRIMÄR`-Beleg | %d (%s) |'
% (sum(1 for a in anf if 'PRIMÄR' in a['belege']),
pct(sum(1 for a in anf if 'PRIMÄR' in a['belege']), n)),
'']
z += ['### Übernahmewürdigkeit', '']
if not mit_uebern:
z += ['**nicht erhoben** – das Feld `Übernahmewürdigkeit` wurde erst mit der '
'Prompt-Fassung vom 2026-08-26 eingeführt und liegt für diesen Lauf nicht vor. '
'Hinweise auf Workarounds stecken ersatzweise im Feld `Status`.', '']
else:
z += ['| Einstufung | Anzahl | Anteil |', '|---|---:|---:|']
for k in ('übernehmen', 'workaround', 'sonderfall', 'veraltet', '(sonstige Angabe)'):
if uebern.get(k):
z.append('| %s | %d | %s |' % (k, uebern[k], pct(uebern[k], n)))
if n - mit_uebern:
z.append('| **ohne Angabe** | %d | %s |' % (n - mit_uebern, pct(n - mit_uebern, n)))
z.append('')
z += ['### Status', '', '| Kategorie | Anzahl | Anteil |', '|---|---:|---:|',
'| belegt | %d | %s |' % (n - len(hyp), pct(n - len(hyp), n)),
'| als `HYPOTHESE` gekennzeichnet | %d | %s |' % (len(hyp), pct(len(hyp), n)),
'| als Workaround vermerkt | %d | %s |' % (len(work), pct(len(work), n)),
'| Konsolidierungskandidaten | %d | %s |' % (len(kons), pct(len(kons), n)),
'| mit ISO-25010-Qualitätsmerkmal | %d | %s |' % (len(mit_qm), pct(len(mit_qm), n)),
'']
z += ['### Regelkonformität (Prüfung gegen die Vorgaben des Prompts)', '',
'| Vorgabe | Ergebnis |', '|---|---|']
z.append('| **Belegpflicht** – jede Anforderung mindestens ein Artefaktbeleg | %s |'
% ('**erfüllt** (0 Anforderungen ohne Beleg)' if not ohne_beleg
else '**verletzt** – %d ohne Beleg: %s' % (len(ohne_beleg),
', '.join(a['id'] for a in ohne_beleg[:8]) + (' …' if len(ohne_beleg) > 8 else ''))))
z.append('| **Risikobasierte Priorisierung** – Sicherheit, Abrechnung, Berechtigungen brauchen '
'einen `PRIMÄR`-Beleg oder die Kennzeichnung `[HYPOTHESE]` | %s |'
% ('**erfüllt** (%d risikorelevante Anforderungen, alle gedeckt)' % len(risiko)
if not risiko_ungedeckt
else '**verletzt** – %d von %d ungedeckt: %s' % (len(risiko_ungedeckt), len(risiko),
', '.join(a['id'] for a in risiko_ungedeckt[:8]) + (' …' if len(risiko_ungedeckt) > 8 else ''))))
z.append('| **Verifizierbarkeit** – jede Anforderung mit Prüfidee oder Akzeptanzkriterium | %s |'
% ('**erfüllt**' if not ohne_pruef
else '**verletzt** – %d ohne Prüfidee' % len(ohne_pruef)))
z.append('| **Übernahmewürdigkeit** – Einstufung für die Migrationsperspektive | %s |'
% ('*nicht erhoben* (Feld erst ab Prompt-Fassung 2026-08-26)' if not mit_uebern
else ('**erfüllt** (alle %d Anforderungen eingestuft)' % n if mit_uebern == n
else '**verletzt** – %d von %d ohne Angabe' % (n - mit_uebern, n))))
z.append('| **Traceability** – Verknüpfung zwischen den Ebenen | %d von %d mit Tracelinks (%s) |'
% (n - len(ohne_trace), n, pct(n - len(ohne_trace), n)))
z.append('')
return '\n'.join(z)
def main():
lauf = sys.argv[1]
anf = analysiere(lauf)
meta = os.path.join(lauf, '_meta')
os.makedirs(meta, exist_ok=True)
io.open(os.path.join(meta, 'anforderungen.json'), 'w', encoding='utf-8').write(
json.dumps(anf, indent=1, ensure_ascii=False))
text = abschnitt(anf)
io.open(os.path.join(meta, 'anforderungen.md'), 'w', encoding='utf-8').write(text + '\n')
if '--print' in sys.argv:
print(text)
else:
print('%s: %d Anforderungen ausgewertet -> _meta/anforderungen.md'
% (os.path.basename(os.path.normpath(lauf))[8:], len(anf)))
if __name__ == '__main__':
main()
@@ -0,0 +1,90 @@
"""Extrahiert Subagenten-Aufrufe (Prompt, Typ, Ergebnislaenge) aus dem persistierten
Session-Transkript eines Headless-Laufs und legt sie im Laufverzeichnis unter _meta ab.
Aufruf: python subagenten.py <laufverzeichnis> [<transkript-wurzel>]
Die Session-ID wird aus <laufverzeichnis>/RawResult.json gelesen.
"""
import io, json, os, sys, glob
lauf = sys.argv[1]
wurzel = sys.argv[2] if len(sys.argv) > 2 else os.path.expandvars(r'%USERPROFILE%\.claude\projects')
roh = json.load(io.open(os.path.join(lauf, 'RawResult.json'), encoding='utf-8-sig'))
sid = roh.get('session_id')
stats = roh.get('subagent_stats') or {}
gesamt = stats.get('spawned', 0)
verschachtelt = stats.get('spawned_by_subagents', 0)
# Im Haupttranskript stehen nur die direkt vom Hauptagenten gestarteten Subagenten.
# Von Subagenten gestartete liegen in deren eigenen Transkripten.
erwartet = gesamt - verschachtelt
treffer = glob.glob(os.path.join(wurzel, '*', sid + '.jsonl'))
if not treffer:
print('KEIN TRANSKRIPT gefunden fuer Session', sid)
sys.exit(1)
pfad = treffer[0]
aufrufe, ergebnisse = [], {}
for zeile in io.open(pfad, encoding='utf-8'):
try:
o = json.loads(zeile)
except Exception:
continue
cont = (o.get('message') or {}).get('content')
if not isinstance(cont, list):
continue
for c in cont:
if not isinstance(c, dict):
continue
if c.get('type') == 'tool_use' and c.get('name') in ('Task', 'Agent'):
ein = c.get('input') or {}
aufrufe.append({
'id': c.get('id'),
'werkzeug': c.get('name'),
'subagent_type': ein.get('subagent_type'),
'description': ein.get('description'),
'run_in_background': ein.get('run_in_background'),
'model': ein.get('model'),
'prompt': ein.get('prompt') or '',
})
elif c.get('type') == 'tool_result':
inhalt = c.get('content')
if isinstance(inhalt, list):
inhalt = ' '.join(str(x.get('text', '')) for x in inhalt if isinstance(x, dict))
ergebnisse[c.get('tool_use_id')] = len(str(inhalt or ''))
for a in aufrufe:
a['ergebnis_zeichen'] = ergebnisse.get(a['id'])
meta = os.path.join(lauf, '_meta')
os.makedirs(meta, exist_ok=True)
io.open(os.path.join(meta, 'subagenten.json'), 'w', encoding='utf-8').write(
json.dumps(aufrufe, indent=1, ensure_ascii=False))
md = ['# Subagenten-Aufrufe', '',
'Session `%s`, Transkript `%s`.' % (sid, os.path.basename(pfad)),
'',
'`subagent_stats`: **%s** Subagenten gesamt, davon **%s** von Subagenten gestartet '
'(max_depth %s). Direkt vom Hauptagenten erwartet: **%s**. Im Transkript gefunden: **%s**.'
% (gesamt, verschachtelt, stats.get('max_depth'), erwartet, len(aufrufe)), '']
if verschachtelt:
md += ['> Die %s von Subagenten gestarteten Aufrufe stehen in deren eigenen Transkripten und'
' sind hier **nicht** enthalten.' % verschachtelt, '']
if erwartet != len(aufrufe):
md += ['> **Abweichung** zwischen erwarteter und gefundener Anzahl.',
'> Ursache pruefen, bevor die Prompts ausgewertet werden.', '']
for i, a in enumerate(aufrufe, 1):
md += ['## %d. %s' % (i, a['description'] or '(ohne Beschreibung)'),
'',
'- **Werkzeug:** `%s` **Typ:** `%s` **Hintergrund:** %s'
% (a['werkzeug'], a['subagent_type'], a['run_in_background']),
'- **Prompt-Zeichen:** %d **Ergebnis-Zeichen:** %s'
% (len(a['prompt']), a['ergebnis_zeichen']),
'', '### Prompt', '', '```', a['prompt'].rstrip(), '```', '']
io.open(os.path.join(meta, 'subagenten.md'), 'w', encoding='utf-8').write('\n'.join(md))
print('Subagenten gefunden: %d (direkt erwartet: %s, gesamt: %s, davon verschachtelt: %s)' % (len(aufrufe), erwartet, gesamt, verschachtelt))
for a in aufrufe:
print(' - %-42s %s Prompt %6d Z. Ergebnis %s Z.'
% ((a['description'] or '')[:42], a['subagent_type'], len(a['prompt']), a['ergebnis_zeichen']))
print('geschrieben:', os.path.join(meta, 'subagenten.md'))
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
{}
@@ -1,2 +0,0 @@
#heading(level: 1, numbering: "0")[Abstract]
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ergänze hier die Zusammenfassung der Arbeit.
+2 -2
View File
@@ -1,2 +1,2 @@
#heading(level: 1, numbering: none)[Abstract (ca. 1 Seite)]
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ergänze hier die Zusammenfassung der Arbeit.
#heading(level: 1, numbering: none)[Abstract]
Zusammenfassung der Arbeit.
+28 -28
View File
@@ -1,20 +1,22 @@
#heading(level: 1)[Einleitung (ca. 8 Seiten)]
#heading(level: 1)[Einleitung]
#heading(level: 2)[Ausgangssituation und Motivation]
In den vergangenen Jahren hat die digitale Transformation mittelständische Softwareanbieter gezwungen, ihre gewachsenen Systeme neu zu bewerten. Besonders ERP-Lösungen, die über Jahrzehnte in Windows-Umgebungen gepflegt wurden, stoßen bei Cloud-, Web- und Mobile-Szenarien an technische sowie organisatorische Grenzen. Dokumentierte Architekturentscheidungen sind selten, implizites Wissen steckt in Source-Control-Systemen oder bei einzelnen Entwickler:innen.
In den vergangenen Jahren hat die digitale Transformation mittelständische Softwareanbieter gezwungen, ihre Produkte neu zu bewerten. Betroffen sind vor allem Systeme, die über lange Jahre ausschließlich in Windows-Umgebungen vertrieben wurden. Diese stoßen bei Cloud-, Web- und Mobile-Szenarien an technische sowie organisatorische Grenzen und häufen zunehmend technische Schulden an. Eine technologische Weiterentwicklung wird zusehends schwieriger, und an einer Neuentwicklung führt oft kein Weg vorbei. Dokumentierte Anforderungen oder eine systematische Code-Dokumentation sind allerdings selten. Der Großteil des Wissens steckt implizit im Code oder in den Köpfen der verbleibenden Entwickler.
Die c-entron GmbH in Ulm repräsentiert diesen Kontext. Das Unternehmen betreibt seit über zwanzig Jahren eine Windows-basierte ERP-Suite für IT-Systemhäuser. Die Lösung deckt Auftragsabwicklung, Lager, Fakturierung und Projektabrechnung ab, ist aber eng mit der bisherigen Client/Server-Architektur gekoppelt. Kunden fordern inzwischen plattformunabhängige Oberflächen, Self-Service-Funktionen und flexible Betriebsmodelle. Die bestehende Anwendung limitiert Skalierung, Deployment und Benutzerführung, wodurch eine Migration auf eine webbasierte Plattform zwingend erforderlich wird.
Die c-entron GmbH in Ulm ist von diesem Szenario unmittelbar betroffen. Das Unternehmen betreibt seit über zwanzig Jahren eine Windows-basierte ERP-Suite für IT-Systemhäuser. Die Lösung deckt Auftragsabwicklung, Lager, Fakturierung und Projektabrechnung ab, ist jedoch eng mit der bisherigen Client/Server-Architektur gekoppelt. Kunden erwarten inzwischen plattformunabhängige Oberflächen, Self-Service-Funktionen und flexible Betriebsmodelle wie SaaS (Software as a Service). Die bestehende Anwendung ist in Skalierung, Deployment und Abrechnung jedoch limitiert, sodass eine Migration auf eine webbasierte Plattform zwingend erforderlich wird.
Parallel dazu hat sich ein neues Instrumentarium etabliert. Large Language Models wie Chat GPT-5 oder Claude.ai können große durch agentische CLIs (Codex, Claude Code) große Mengen an Quellcode analysieren, Muster erkennen und textuell beschreiben. Damit entsteht die Chance, fehlende Anforderungsdokumentationen zumindest teilweise aus dem Code heraus zu rekonstruieren. Die praktische Nutzung dieses Potenzials ist bislang kaum erforscht – insbesondere nicht in mittelständischen Legacy-Projekten. Diese Arbeit adressiert genau diese Lücke und untersucht, wie KI-gestützte Verfahren für eine systematische Anforderungsextraktion eingesetzt werden können.
Eine Neuimplementierung auf Basis vorhandener Anforderungs- oder Code-Dokumentation ist aus den oben genannten Gründen kaum möglich. Die Herausforderung besteht darin, mit möglichst geringem Aufwand eine vollständige Beschreibung eines komplexen ERP-Systems zu erarbeiten. Eine manuelle Auswertung von Code und Oberflächen auf Funktionalitäten ist angesichts der Komplexität, die aus der langjährigen Weiterentwicklung resultiert, nur mit sehr hohem Personalaufwand möglich und daher nicht realisierbar.
In den vergangenen Jahren hat sich hierzu ein neues Instrumentarium etabliert. Large Language Models wie ChatGPT-5 oder Claude.ai können in Verbindung mit agentischen CLIs (Codex, Claude Code) große Mengen an Quellcode analysieren, Anforderungen erarbeiten und textuell beschreiben. Damit entsteht die Chance, fehlende Anforderungsdokumentationen zumindest teilweise aus dem Code heraus zu rekonstruieren. Die praktische Nutzung dieses Potenzials ist bislang kaum erforscht. Diese Arbeit setzt an dieser Stelle an und untersucht, wie KI-gestützte Verfahren für eine systematische Anforderungsanalyse eingesetzt werden können.
#heading(level: 2)[Problemstellung]
Im Projektumfeld der c-entron GmbH fehlen strukturierte Requirements für die bestehende ERP-Lösung. Die Analyse der Legacy-Codebasis ist zeitintensiv, personengebunden und anfällig für Auslassungen. Daraus ergeben sich mehrere Risiken:
Für das ERP-Software-Produkt der c-entron fehlen strukturierte und dokumentierte Requirements. Die Analyse der bestehenden Codebasis ist zeit- und ressourcenintensiv sowie anfällig für Insel- und Metawissen. Daraus ergeben sich mehrere Risiken:
- **Re-Implementationsfehler:** Edge Cases, Workarounds und kundenindividuelle Anpassungen sind nur im Code sichtbar. Ohne vollständige Erfassung drohen Funktionsverluste nach der Migration.
- **Technische Schuld:** Entwickler:innen investieren viel Zeit in das Verständnis historischer Strukturen, statt aktiv an der neuen Plattform zu arbeiten. Veraltete Muster werden unreflektiert übernommen.
- **Implizites Wissen:** Domänenwissen liegt bei wenigen langjährigen Mitarbeitenden. Personalwechsel führen zu Wissensverlust und Verzögerungen.
- **Komplexität der Codebasis:** Verschachtelte Abhängigkeiten, unterschiedliche Stile und technologiebedingte Zwänge erschweren eine modulare Anforderungsableitung.
- **Fehlende Traceability:** Ohne Zuordnung zwischen Code und Geschäftsprozess fehlt die Grundlage für Priorisierung, Testkonzeption und spätere Wartung.
/ Re-Implementationsfehler: Edge Cases, Workarounds und kundenindividuelle Anpassungen sind nur im Code sichtbar. Ohne vollständige Erfassung drohen Funktionsverluste nach der Migration. Workarounds sind zugleich Symptom einer mangelhaften Anforderungserfassung in der ursprünglichen Implementierung und Zeichen fehlender Weitsicht.
/ Technische Schuld: Entwickler investieren viel Zeit in das Verständnis historischer Strukturen, statt aktiv an der neuen Plattform zu arbeiten. Veraltete Muster werden unreflektiert übernommen. Neue Mitarbeiter sind mit den alten Technologien nicht vertraut, und es fehlt ihnen das Verständnis für historische Zwänge und Zusammenhänge.
/ Implizites Wissen: Domänenwissen liegt bei wenigen langjährigen Mitarbeitenden. Personalwechsel führen zu Wissensverlust und Verzögerungen. Gleichzeitig führt die langjährige Arbeit mit dem bestehenden System zu eingeschränkter Offenheit beim Design neuer Lösungen („Das haben wir schon immer so umgesetzt").
/ Komplexität der Codebasis: Verschachtelte Abhängigkeiten, unterschiedliche Stile und technologiebedingte Zwänge erschweren eine modulare Anforderungsableitung.
/ Fehlende Traceability: Ohne Zuordnung zwischen Code und Geschäftsprozess fehlt die Grundlage für Priorisierung, Testkonzeption und spätere Wartung. Große Teile des Codes sind zudem generisch und lassen sich keiner konkreten Anforderung zuordnen, etwa das Anzeigen einer Tabelle. Konkrete Datenflüsse lassen sich nur am laufenden System beobachten, was die Analyse um eine weitere Größenordnung ressourcenintensiver macht.
Eine rein manuelle Rekonstruktion aller Anforderungen wäre wirtschaftlich kaum tragbar. Deshalb soll geprüft werden, ob KI-gestützte Verfahren Requirements so extrahieren können, dass sie als belastbare Basis für die Modernisierung dienen.
@@ -23,29 +25,27 @@ Diese Arbeit verfolgt das Ziel, ein vollständiges Vorgehen für KI-gestütztes
- Entwicklung eines Prozessmodells, das Vorbereitung, Analyse, Validierung und Übergabe strukturiert.
- Evaluation aktueller LLMs hinsichtlich Kontextfenster, Codeverständnis, Steuerbarkeit, Kosten und Datenschutz.
- Durchführung und Vergleich von drei Claude-Code-basierten Versuchen (V01-V03) mit unterschiedlicher Tooling-Tiefe (Prompt-only, Agenten, Agenten+MCP).
- Integration von Stakeholder-Wissen durch Interviews, um nicht direkt aus dem Code ableitbare Anforderungen zu ergänzen.
- Definition eines Evaluationsrahmens mit quantitativen und qualitativen Kriterien (Vollständigkeit, Verständlichkeit, Redundanzfreiheit, Aufwandseinsparung).
- Formulierung konkreter Handlungsempfehlungen für die c-entron GmbH sowie Übertragbarkeit auf ähnliche Unternehmen.
- Integration von Stakeholder-Wissen durch Interviews, um die Qualität der KI-Ergebnisse zu bewerten und nicht direkt aus dem Code ableitbare Anforderungen zu ergänzen.
#heading(level: 2)[Forschungsleitfragen]
Die Zielsetzung wird über vier Forschungsleitfragen strukturiert:
Diese Zielsetzung wird über vier Forschungsleitfragen strukturiert:
- **F1 - Einsatz von LLMs im Reverse Requirements Engineering:** Welche Prozessschritte, Steuerungsmechanismen und Kontrollpunkte sind notwendig, um LLMs reproduzierbar einzusetzen?
- **F2 - Kombination von KI-Analyse und Stakeholder-Input:** Welche funktionalen und nicht-funktionalen Anforderungen lassen sich aus Code extrahieren, und welche Informationen müssen über Interviews ergänzt werden?
- **F3 - Qualitätsbewertung der generierten Requirements:** Wie beurteilen Fachexperten Vollständigkeit, Verständlichkeit, Nützlichkeit und Aufwandseinsparung der KI-Ergebnisse?
- **F4 - Chancen und Grenzen des Ansatzes:** Welche Effizienzgewinne sind realistisch, wo liegen technische oder organisatorische Limitierungen, und welche Risiken (z. B. Halluzinationen, Datenschutz) müssen adressiert werden?
1. *Einsatz von LLMs im Reverse Requirements Engineering:* Welche Prozessschritte, Steuerungsmechanismen und Kontrollpunkte sind notwendig, um LLMs reproduzierbar einzusetzen?
2. *Kombination von KI-Analyse und Stakeholder-Input:* Welche funktionalen und nicht-funktionalen Anforderungen lassen sich aus Code extrahieren, und welche Informationen müssen über Interviews ergänzt werden?
3. *Qualitätsbewertung der generierten Requirements:* Wie beurteilen Fachexperten Vollständigkeit, Verständlichkeit, Nützlichkeit und Aufwandseinsparung der KI-Ergebnisse?
4. *Chancen und Grenzen des Ansatzes:* Welche Effizienzgewinne sind realistisch, wo liegen technische oder organisatorische Limitierungen, und welche Risiken (z. B. Halluzinationen, Datenschutz) müssen adressiert werden?
#heading(level: 2)[Aufbau der Arbeit]
Die Arbeit ist in acht Kapitel gegliedert und folgt dem in den Vorlagen üblichen Aufbau:
Die Arbeit ist in acht Kapitel gegliedert:
1. **Einleitung:** Kontext, Problemstellung, Ziele und Forschungsfragen.
2. **Theoretische Grundlagen:** Requirements Engineering, Reverse Engineering, Large Language Models sowie Qualitätssicherungskriterien.
3. **Fallstudie c-entron GmbH:** Unternehmensprofil, Produktarchitektur, Migrationsdruck und Rahmenbedingungen.
4. **Konzeption und methodisches Vorgehen:** Prozessmodell, Technologieauswahl, Stakeholder-Einbindung und Datenbasis.
5. **Ergebnisse:** Vollständige Ergebnisdarstellung der drei Versuche inkl. Artefaktlisten und beispielhafter Requirements/Use Cases aus den Ergebnisverzeichnissen.
6. **Evaluation:** Vorgehen, Metriken, Ergebnisse und Expertenfeedback.
7. **Diskussion:** Interpretation der Resultate, Limitationen und Implikationen für Forschung und Praxis.
8. **Fazit und Ausblick:** Zusammenfassung, Beantwortung der Forschungsfragen und Perspektiven für weitere Arbeiten.
1. *Einleitung:* Kontext, Problemstellung, Ziele und Forschungsfragen.
2. *Theoretische Grundlagen:* Requirements Engineering, Reverse Engineering, Large Language Models sowie Qualitätssicherungskriterien.
3. *Fallstudie c-entron GmbH:* Unternehmensprofil, Produktarchitektur, Migrationsdruck und Rahmenbedingungen.
4. *Konzeption und methodisches Vorgehen:* Prozessmodell, Technologieauswahl, Stakeholder-Einbindung und Datenbasis.
5. *Ergebnisse:* Vollständige Ergebnisdarstellung der drei Versuche inkl. Artefaktlisten und beispielhafter Requirements / Use Cases aus den Ergebnisverzeichnissen.
6. *Evaluation:* Vorgehen, Metriken, Ergebnisse und Expertenfeedback.
7. *Diskussion:* Interpretation der Resultate, Limitationen und Implikationen für Forschung und Praxis.
8. *Fazit und Ausblick:* Zusammenfassung, Beantwortung der Forschungsfragen und Perspektiven für weitere Arbeiten.
Damit entsteht eine nachvollziehbare Linie von der Ausgangssituation über das Konzept bis zur Validierung.
+8 -365
View File
@@ -4,370 +4,13 @@
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Theoretische Grundlagen (ca. 12 Seiten)]
#heading(level: 1)[Theoretische Grundlagen]
Dieses Kapitel beschreibt die theoretischen Grundlagen, die für die Konzeption und Bewertung eines KI-gestützten Reverse Requirements Engineering in Legacy-Umgebungen benötigt werden. Zunächst werden zentrale Begriffe des Requirements Engineering sowie die Idee der rückwärtsgerichteten Anforderungsgewinnung aus bestehenden Systemen eingeordnet. Anschließend werden Large Language Models als Werkzeugklasse im Software Engineering beschrieben, inklusive typischer Leistungsgrenzen und Absicherungsmechanismen. Abschließend werden Grundlagen der Legacy-Modernisierung sowie etablierte Migrationsstrategien zusammengefasst, um den Kontext der Fallstudie und die Zielrichtung einer webbasierten Modernisierung einzuordnen.
Dieses Kapitel beschreibt die theoretischen Grundlagen, die für die Konzeption und Bewertung eines KI-gestützten Reverse Requirements Engineering in Legacy-Umgebungen benötigt werden. Zunächst werden zentrale Themen des Requirements Engineerings sowie die Idee des reverse Requirements Engineerings auf Basis bestehender Systeme eingeordnet. Anschließend werden Large Language Models und deren Einsatz im Software Engineering inklusive typischer Leistungsgrenzen und Absicherungsmechanismen beschrieben. Abschließend werden Grundlagen der Legacy-Modernisierung sowie etablierte Migrationsstrategien zusammengefasst, um den Kontext der Fallstudie und die Zielrichtung einzuordnen.
#heading(level: 2)[Requirements Engineering und Reverse Requirements Engineering]
#heading(level: 3)[Begriff und Zielsetzung des Requirements Engineering]
Requirements Engineering (RE) umfasst die systematische Erhebung, Analyse, Spezifikation, Validierung und Verwaltung von Anforderungen an ein System über dessen Lebenszyklus. In Standards und Lehrwerken wird RE als eigenständiger Prozess verstanden, der sowohl fachliche Ziele (z. B. unterstützte Geschäftsprozesse) als auch technische und organisatorische Randbedingungen (z. B. Sicherheitsvorgaben, Betriebsmodelle) in überprüfbare Aussagen überführt @iso29148_2018 @ieee830_1998.
Im Kern adressiert RE zwei Spannungsfelder:
- **Kommunikation zwischen Domäne und Technik:** Anforderungen müssen fachlich verständlich und gleichzeitig so präzise sein, dass sie implementiert, getestet und geändert werden können.
- **Umgang mit Unsicherheit und Wandel:** Anforderungen sind zu Projektbeginn selten vollständig. RE ist daher nicht nur Dokumentation, sondern ein iterativer Klärungs- und Abstimmungsprozess.
Ein etablierter Ansatz zur Strukturierung heterogener Sichtweisen ist das Viewpoint-Konzept, bei dem Anforderungen aus unterschiedlichen Perspektiven modelliert und anschließend konsolidiert werden @kotonya1996viewpoints. Für die vorliegende Arbeit ist diese Perspektivenorientierung relevant, weil eine Codebasis typischerweise keine expliziten Stakeholder-Sichten enthält, diese aber für eine Migration wieder sichtbar gemacht werden müssen (z. B. Nutzerrollen, kundenspezifische Varianten, regulatorische Vorgaben).
#heading(level: 3)[Arten von Requirements und Qualitätskriterien]
In der Literatur wird häufig zwischen funktionalen Anforderungen (Was soll das System tun?) und Qualitäts- bzw. nicht-funktionalen Anforderungen (Welche Eigenschaften und Randbedingungen gelten?) unterschieden. Die Praxis zeigt jedoch, dass diese Trennung nicht immer trennscharf ist: Eigenschaften können sowohl als Systemverhalten (z. B. „Audit-Log erzeugen“) als auch als Qualitätsziel (z. B. „Nachvollziehbarkeit“) formuliert werden @glinz2007nfr. Für Reverse Requirements Engineering ist diese Unschärfe besonders relevant, weil Quellcode meist Verhalten konkretisiert, Qualitätsziele aber häufig implizit bleiben (z. B. Performance-Workarounds, Sicherheitsannahmen).
Für die Qualität einzelner Requirements sind in Standards und RE-Forschung wiederkehrende Kriterien etabliert. ISO/IEC/IEEE 29148:2018 nennt unter anderem Eindeutigkeit, Konsistenz, Vollständigkeit, Verifizierbarkeit und Nachvollziehbarkeit als zentrale Eigenschaften. IEEE 830-1998 formuliert ähnliche Prinzipien für Software Requirements Specifications, mit stärkerem Fokus auf Dokumentstruktur und Lesbarkeit @iso29148_2018 @ieee830_1998.
Für die Bewertung von KI-extrahierten Requirements sind drei Kriterien unmittelbar handhabbar:
- **Verifizierbarkeit:** Ein Requirement ist so formuliert, dass eine Testidee oder Prüfmethode ableitbar ist (z. B. Messkriterium, Akzeptanzbedingung).
- **Eindeutigkeit:** Formulierungen vermeiden Mehrdeutigkeiten und definieren Begriffe, die in der Domäne unterschiedlich interpretiert werden können.
- **Nachvollziehbarkeit (Traceability):** Es ist erkennbar, aus welchem Artefakt (Code, Konfiguration, Datenbank, Ticket, Interview) das Requirement abgeleitet wurde.
Qualitätsanforderungen verdienen im Modernisierungskontext eine gesonderte Betrachtung, weil sie über die reine Funktionsgleichheit hinaus die Zielarchitektur motivieren. #cite(<glinz2008quality>, form: "prose") argumentiert, dass Qualitätsanforderungen risikobasiert und wertorientiert priorisiert werden sollten. Für Legacy-Migrationen ist dies plausibel: Ein „vollständiges“ Requirements-Set ist praktisch schwer erreichbar, gleichzeitig sind bestimmte Quality Requirements (z. B. Datenschutz, Verfügbarkeit, Rollout-Fähigkeit) hochkritisch, weil sie Architekturentscheidungen dominieren.
Für die inhaltliche Strukturierung von Qualitätsanforderungen ist das Qualitätsmodell ISO/IEC 25010:2011 verbreitet, das Qualitätsmerkmale wie Performance-Effizienz, Zuverlässigkeit, Sicherheit oder Wartbarkeit systematisch ordnet. Für Reverse Requirements Engineering ist dies hilfreich, weil aus Code häufig nur Teilaspekte sichtbar werden (z. B. Caching-Mechanismen als Hinweis auf Performance-Annahmen), während andere Qualitätsziele (z. B. „Maintainability“) eher indirekt über Architekturentscheidungen und Entwicklungspraktiken wirksam werden @iso25010_2011.
Die Relevanz sauberer Requirements-Qualität zeigt sich auch in der Risikoperspektive. #cite(<lawrence2001toprisk>, form: "prose") beschreiben Requirements Engineering als primäre Risikozone, wenn Anforderungen unklar, instabil oder unvollständig sind. Für diese Arbeit folgt daraus, dass KI-gestützte Requirements-Extraktion nicht nur „mehr Text“ erzeugen darf, sondern gezielt die Risiken der Unklarheit und der Fehlinterpretation reduzieren muss.
#heading(level: 3)[Spezifikationsformen und Grad der Formalisierung]
Requirements werden in der Praxis in unterschiedlichen Repräsentationsformen dokumentiert. Standards wie IEEE 830-1998 und ISO/IEC/IEEE 29148:2018 fokussieren auf strukturierte Spezifikationen (z. B. SRS) und definieren typische Kapitel (Zweck, Systemkontext, funktionale Anforderungen, Schnittstellen, Qualitätsanforderungen, Annahmen). Daneben existieren weniger formale Formen wie User Stories, Use-Case-Beschreibungen oder Backlog-Einträge, die vor allem in agilen Settings verbreitet sind @ieee830_1998 @iso29148_2018.
Für Reverse Requirements Engineering sind zwei Punkte entscheidend:
- **Form beeinflusst Interpretierbarkeit:** Eine knappe User Story („Als Nutzer möchte ich …“) ist leicht verständlich, transportiert aber selten Randbedingungen, Datenregeln oder Fehlerfälle. Eine SRS-Formulierung kann präziser sein, erfordert aber mehr Kontext und Definitionen.
- **Grad der Formalisierung beeinflusst Prüfbarkeit:** Je stärker Requirements mit Akzeptanzkriterien, Beispielen oder Messgrößen verknüpft sind, desto einfacher sind Reviews und Tests. #cite(<pohl2010re>, form: "prose") betont Anforderungen-Validierung als eigene Disziplin, die ohne prüfbare Formulierungen methodisch kaum belastbar ist.
Im Kontext dieser Arbeit bietet sich daher ein hybrider Stil an: Requirements werden als kurze, klare Soll-Aussagen formuliert und jeweils um Kontext (Akteur/Prozess), Randbedingungen (Vorbedingungen, Datenobjekte) und mindestens eine Prüfidee ergänzt. LLMs können die sprachliche Konsistenz unterstützen, die notwendige Präzisierung muss jedoch durch Belege und Validierung abgesichert werden.
#heading(level: 3)[Traceability als Verbindung zwischen Code und Requirement]
Traceability bezeichnet die Möglichkeit, Beziehungen zwischen Requirements und anderen Artefakten herzustellen und über den Lebenszyklus zu pflegen. #cite(<gotel1994traceability>, form: "prose") analysieren Traceability als wiederkehrendes Problem, insbesondere dort, wo Artefakte heterogen sind und die Disziplin zur Pflege fehlt. #cite(<ramesh2001traceability>, form: "prose") schlagen Referenzmodelle vor, die Traceability-Typen und -Ziele strukturieren, etwa die Rückverfolgbarkeit zur Begründung (Rationale), zu Designentscheidungen oder zur Evolution eines Requirements.
Für Reverse Requirements Engineering ist Traceability nicht nur ein „Nice-to-have“, sondern eine Sicherheitsmaßnahme:
- **Plausibilisierung:** Ein Requirement lässt sich gegen konkrete Codeausschnitte oder Laufzeitbeobachtungen prüfen.
- **Abgrenzung:** Es wird klar, ob eine Aussage wirklich aus der Codebasis folgt oder aus Interpretationen und Ergänzungen entsteht.
- **Änderungsmanagement:** Bei Codeänderungen lässt sich ermitteln, welche Requirements betroffen sein könnten.
In Legacy-Systemen ist Traceability typischerweise fragmentiert: Hinweise finden sich in Commit-Messages, Branch-Namen, Datenbankskripten, Konfigurationsdateien, UI-Texten oder in impliziten Konventionen. Der methodische Anspruch dieser Arbeit besteht daher nicht darin, „perfekte“ Traceability wiederherzustellen, sondern eine minimal belastbare, reproduzierbare Verknüpfung zwischen extrahierten Requirements und Belegen zu etablieren.
#heading(level: 3)[Reverse Engineering und Reverse Requirements Engineering]
Reverse Engineering wird klassisch als Analyseprozess verstanden, der aus einem bestehenden System Wissen über Struktur, Verhalten und Designentscheidungen rekonstruiert. #cite(<chikofsky1990taxonomy>, form: "prose") prägen hierfür eine Taxonomie und grenzen Reverse Engineering von Reengineering sowie Design Recovery ab. Für Requirements-nahe Fragestellungen ist hier relevant, dass Reverse Engineering nicht automatisch „Anforderungen“ liefert, sondern zunächst technische Fakten (z. B. Abhängigkeiten, Datenflüsse, Zustandsautomaten).
Reverse Requirements Engineering (RRE) fokussiert auf die rückwärtsgerichtete Gewinnung von Anforderungen aus bestehenden Artefakten. Dabei kann das Ziel unterschiedlich interpretiert werden:
- **Rekonstruktion eines Soll-Zustands:** Welche fachlichen Anforderungen werden durch die aktuelle Implementierung implizit erfüllt?
- **Rekonstruktion eines Ist-Zustands:** Welche Funktionen und Regeln sind tatsächlich implementiert, unabhängig davon, ob sie intendiert waren?
Gerade im Migrationskontext ist diese Unterscheidung entscheidend. Die Codebasis enthält oft historisch entstandene Workarounds oder kundenspezifische Anpassungen. Diese können fachlich gewollt, technisch opportunistisch oder schlicht „mitgewachsen“ sein. Ohne zusätzliche Validierung besteht das Risiko, dass RRE den Ist-Zustand als Soll-Zustand fehlinterpretiert.
Frühe Ansätze zur Brücke zwischen Reverse Engineering und Requirements liefern beispielsweise #cite(<yu2005retr>, form: "prose") mit „RETR: Reverse Engineering to Requirements“. Der Beitrag betont, dass Requirements-Rückgewinnung eine methodische Kette aus Artefaktsichtung, Strukturierung und Validierung benötigt. In ähnlicher Richtung beschreibt ein requirementsgetriebenes Reengineering-Framework, wie Requirements als Leitplanken für Reengineering-Entscheidungen genutzt werden können @tahvildari2001reengineering.
Methodisch lassen sich dabei grob zwei Analysestränge unterscheiden:
- **Statische Analyse:** Ableitung von Struktur- und Datenflussinformationen aus Code und Artefakten ohne Ausführung (z. B. Abhängigkeiten, SQL-Statements, Aufrufketten). Statische Analyse ist skaliert gut, erkennt aber nicht zuverlässig Laufzeitbedingungen (z. B. Feature Flags, Konfigurationsvarianten).
- **Dynamische Analyse:** Beobachtung von Laufzeitverhalten durch Logging, Tracing oder instrumentierte Tests (z. B. welche Regeln bei bestimmten Eingaben greifen). Dynamische Analyse ist näher am realen Verhalten, benötigt aber reproduzierbare Szenarien und Testdaten.
Reverse Requirements Engineering in einem Migrationsprojekt profitiert typischerweise von einer Kombination beider Stränge. Ohne dynamische Belege steigt das Risiko, dass nicht offensichtliche Bedingungen (z. B. kundenspezifische Schalter) übersehen werden; ohne statische Analyse bleibt die Abdeckung häufig zu gering.
#heading(level: 3)[Typische Methodenkette für Requirements-Rückgewinnung aus Code]
Aus Sicht dieser Arbeit lässt sich Reverse Requirements Engineering in einer Legacy-Codebasis als wiederholbarer Ablauf strukturieren. Die konkrete Ausgestaltung hängt vom System und den verfügbaren Artefakten ab, die grundlegenden Schritte sind jedoch weitgehend stabil:
1. **Scope und Domänenabgrenzung:** Auswahl relevanter Module, Datenobjekte und Prozesse (z. B. Auftragsabwicklung, Fakturierung).
2. **Artefakterhebung:** Quellcode, Konfiguration, UI-Texte, Datenbankschemata, Schnittstellenbeschreibungen, Change-Historie.
3. **Technische Analyse:** Struktur- und Abhängigkeitsanalyse, Identifikation von Kernkomponenten, Regeln und Integrationspunkten.
4. **Semantische Interpretation:** Ableitung fachlicher Aussagen aus technischen Implementierungen (z. B. Statusübergänge, Berechtigungsprüfungen).
5. **Formalisierung als Requirements:** Überführung in klare, testbare Anforderungen mit Kontext (Akteur, Vorbedingung, Ergebnis).
6. **Traceability-Anreicherung:** Verknüpfung jedes Requirements mit Belegen (Datei, Klasse, Methode, SQL-Statement, UI-String).
7. **Validierung:** Review durch Fachexperten und Abgleich mit Laufzeitverhalten, Tickets oder Kundenwissen.
In der Praxis unterscheiden sich Artefakte darin, wie direkt sie fachliche Aussagen stützen. Quellcode, der eine Regel hart erzwingt (z. B. „Update nur bei Status X“), ist als Beleg stärker als Kommentare oder UI-Texte, die lediglich Absichten ausdrücken. Für eine belastbare Requirementsbasis ist es daher sinnvoll, Belege zu klassifizieren und die Aussagekraft zu kennzeichnen, beispielsweise:
- **Primärbelege:** Durchgesetzte Regeln im Code oder in Datenbankconstraints (z. B. Statusmaschinen, Validierungslogik, Berechtigungschecks).
- **Sekundärbelege:** Indirekte Hinweise wie UI-Labels, Fehlermeldungen, Report-Layouts, Mappingtabellen oder Konfigurationsschalter.
- **Kontextbelege:** Ticketbeschreibungen, Commit-Messages oder Interviewaussagen, die Motivation und Ausnahmen erklären, aber nicht zwingend im Code sichtbar sind.
Diese Einteilung ist kein Selbstzweck. Sie hilft, Risiken sichtbar zu machen: Requirements, die überwiegend auf Sekundär- oder Kontextbelegen beruhen, sind anfälliger für Fehlinterpretation und sollten priorisiert validiert werden. Gerade in ERP-Systemen sind Datenbankschemata und SQL-Statements häufig besonders aussagekräftig, weil sie Domänenobjekte, Kardinalitäten und Geschäftsregeln (z. B. referentielle Integrität, historisierte Tabellen) sichtbar machen, die in UI- oder Servicecode nur indirekt erscheinen.
Ein weiterer Hebel ist das Mining der Änderungshistorie. Commit-Messages, Diff-Hotspots oder Branch-Konventionen können Hinweise liefern, welche Bereiche besonders volatil sind, welche Kundenvarianten existieren und wo in der Vergangenheit Fehler oder Workarounds eingeführt wurden. Für Reverse Requirements Engineering folgt daraus, dass Requirements nicht nur „aus dem aktuellen Code“, sondern idealerweise auch aus der Evolution des Codes abgeleitet werden, um implizite Stabilitätsannahmen und technische Schulden zu erkennen.
Der kritische Schritt ist die semantische Interpretation. Program Comprehension ist hierfür das methodische Fundament: #cite(<storey2005program>, form: "prose") zeigt, dass Programmverständnis in der Praxis aus einer Kombination von statischer Analyse, Navigation, Visualisierung und Hypothesenbildung besteht. RRE übernimmt diesen kognitiven Prozess, erweitert ihn jedoch um das Ziel, Aussagen als Requirements zu formulieren, die unabhängig vom Code als Spezifikation nutzbar sind.
#heading(level: 3)[Zwischenfazit zu 2.1]
Requirements Engineering liefert Kriterien und Artefaktformen, um Anforderungen präzise, prüfbar und nachvollziehbar zu beschreiben @iso29148_2018 @ieee830_1998. Reverse Requirements Engineering überträgt diese Zielsetzung in einen Kontext, in dem Requirements nicht vorliegen, sondern aus technischen Artefakten rekonstruiert werden. Für die vorliegende Arbeit folgt daraus, dass Automatisierung (z. B. durch KI) nur dann praktikabel ist, wenn Traceability und Validierung als feste Prozessbestandteile mitgeführt werden.
#heading(level: 2)[Large Language Models im Software Engineering]
#heading(level: 3)[Künstliche Intelligenz, Machine Learning und Einordnung von LLMs]
Künstliche Intelligenz (KI) ist ein Oberbegriff für Verfahren, die Aufgaben bearbeiten, die in der Praxis typischerweise kognitive Fähigkeiten erfordern (z. B. Klassifikation, Planung, Sprachverarbeitung). Machine Learning (ML) ist dabei ein Teilgebiet, das Modelle aus Daten lernt, anstatt Regeln vollständig manuell zu spezifizieren. In der gängigen Einordnung wird zwischen überwachtem Lernen (mit Zielwerten), unüberwachtem Lernen (Struktur in Daten) und Reinforcement Learning (Lernen über Rückmeldesignale) unterschieden @bishop2006prml @goodfellow2016dl.
Deep Learning bezeichnet ML-Verfahren, die neuronale Netze mit vielen Parametern und mehreren Verarbeitungsebenen nutzen, um geeignete Repräsentationen aus Rohdaten zu lernen. Charakteristisch ist, dass Merkmalsextraktion und Modellanpassung gemeinsam über Optimierung (typischerweise Gradientenverfahren) erfolgen. #cite(<lecun2015deeplearning>, form: "prose") beschreiben Deep Learning als zentrale Entwicklungslinie moderner KI, insbesondere für Wahrnehmungs- und Sprachaufgaben.
Neuronale Netze lassen sich dabei vereinfacht als parametrisierte Funktionsketten aus Schichten beschreiben, die Eingaben in zunehmend abstrakte Repräsentationen überführen. Das Training erfolgt über eine Zielfunktion (Loss) und Gradientenberechnung, praktisch meist über Backpropagation und Varianten des Gradientenabstiegs @goodfellow2016dl.
#figure(
image("../Abbildungen/abb_2_1_feedforward_nn.png", width: 85%),
caption: [Schematische Darstellung eines vollständig verbundenen Feedforward-Netzes.]
)
Ein einzelnes Neuron lässt sich als affine Transformation mit nachgeschalteter Aktivierungsfunktion formulieren:
$
z = sum_(i=1)^d w_i x_i + b, #h(1em) a = phi (z)
$
Typische Aktivierungsfunktionen sind die Sigmoid-Funktion und ReLU #cite(<goodfellow2016dl>):
$
sigma (z) = 1 / (1 + e^(-z)), #h(1em) op("ReLU") (z) = op("max") (0, z)
$
#figure(
image("../Abbildungen/abb_2_2_aktivierungsfunktionen.png", width: 85%),
caption: [Beispielhafte Aktivierungsfunktionen (Sigmoid, tanh, ReLU).]
)
Die Optimierung erfolgt üblicherweise iterativ. Für Gradientenabstieg gilt in kompakter Form:
$
theta^(t+1) = theta^(t) - eta nabla_theta cal(L) (theta^(t))
$
**Abgrenzung neuronaler Netze und LLMs zu anderen ML-Methoden**
Neuronale Netze sind ein Teilbereich von ML, sie ersetzen jedoch nicht automatisch klassische Verfahren. In der Praxis hängt die Methodenauswahl von Datenart, Datenmenge, Interpretierbarkeit und Betriebsvorgaben ab @bishop2006prml @hastie2009esl.
Die folgende Tabelle fasst die Abgrenzung zu häufigen ML-Familien zusammen:
#figure(
kind: "table",
supplement: [Tabelle],
caption: [Abgrenzung zu häufigen Machine-Learning-Methoden.],
table(
columns: (1fr, 1.35fr, 1.2fr, 1.6fr),
align: (left, left, left, left),
[**Methodik**],
[**Typischer Einsatz**],
[**Stärken**],
[**Grenzen**],
[Lineare/GLM-Modelle],
[Strukturierte Daten, Baselines],
[schnell, gut interpretierbar],
[begrenzte Nichtlinearität (ohne Feature Engineering)],
[Support Vector Machines (SVM)],
[Klassifikation/Regression, mittlere Datenmengen],
[starke Theorie, robuste Margin-Idee],
[Skalierung/Kernelwahl, eingeschränkte Erklärbarkeit @cortes1995svm],
[Entscheidungsbäume/Ensembles],
[Tabellarische Daten],
[nichtlinear, oft gute Performance],
[Overfitting ohne Regularisierung; Ensembles weniger interpretierbar],
[Random Forests],
[Tabellarische Daten, robuste Defaults],
[stabil, gute Generalisierung],
[begrenzte Extrapolation, Erklärbarkeit indirekt @breiman2001randomforests],
[Gradient Boosting],
[Tabellarische Daten, hohe Genauigkeit],
[sehr starke Praxisleistung],
[Hyperparameter-sensitiv, Trainingskosten @friedman2001gbm],
[Neuronale Netze (Deep Learning)],
[Unstrukturierte Daten (Text, Bild), große Datenmengen],
[Representation Learning, End-to-End],
[hoher Daten-/Rechenbedarf, schwerer zu erklären @lecun2015deeplearning],
[LLMs (Transformers)],
[Text- und Codeaufgaben, generative Assistenz],
[Vortraining nutzt große Korpora; flexible Transferleistung],
[Halluzinationen, Kontextlimit, Governance-Aufwand @vaswani2017attention @ji2023hallucination],
)
)
LLMs unterscheiden sich dabei von vielen klassischen Verfahren nicht nur durch Modellgröße, sondern auch durch Zielsetzung: Häufig wird ein generatives, autoregressives Sprachmodell trainiert, das die nächste Tokenwahrscheinlichkeit modelliert:
$
max_(theta) sum_(t=1)^T log p_(theta) (x_t mid x_(<t))
$
Für das Requirements Engineering ist diese Abgrenzung wichtig, weil LLMs aufgrund ihrer generativen Natur Texte produzieren können, die sprachlich konsistent wirken, aber fachlich falsch sein können. Klassische Modelle liefern in solchen Fällen eher „falsche Vorhersagen“, erzeugen jedoch nicht ohne weiteres neue, plausibel klingende Spezifikationen.
Für die Sprachverarbeitung ist der Begriff "Sprachmodell" relevant: Ein Sprachmodell schätzt Wahrscheinlichkeiten über Tokenfolgen und kann dadurch Texte fortsetzen oder bewerten. Large Language Models (LLMs) sind eine Ausprägung solcher Sprachmodelle, die auf Deep-Learning-Architekturen beruhen und auf sehr großen Korpora vortrainiert werden. In der aktuellen Modellgeneration dominiert die Transformer-Architektur, deren Kernprinzip die Self-Attention ist @vaswani2017attention.
Self-Attention lässt sich im Transformer formal als gewichtete Kombination von Value-Vektoren beschreiben, wobei die Gewichte aus Query-Key-Ähnlichkeiten berechnet werden #cite(<vaswani2017attention>):
$
op("Attention") (Q, K, V) = op("softmax") ((Q K^T) / sqrt(d_k)) V
$
#figure(
image("../Abbildungen/abb_2_3_attention_heatmap.png", width: 85%),
caption: [Schematisches Beispiel einer Attention-Gewichtsmatrix (illustrativ).]
)
Für diese Arbeit sind drei Konsequenzen dieser Modellklasse besonders relevant:
- **Kontextfenster:** Modelle verarbeiten Eingaben nur bis zu einer maximalen Tokenanzahl. Längere Artefakte müssen segmentiert oder komprimiert werden.
- **Tokenisierung:** Quellcode und Fachsprache werden in Token zerlegt. Dies beeinflusst, wie gut Identifier, Struktur und Domänenterminologie repräsentiert werden.
- **Generativer Charakter:** Ausgaben sind nicht deterministisch. Temperatur, Sampling-Strategien und Promptform beeinflussen Reproduzierbarkeit.
LLMs werden im Software Engineering eingesetzt, weil sie sowohl natürlichsprachliche Artefakte (z. B. Anforderungen, Kommentare) als auch Codeartefakte (z. B. Klassen, Funktionen, Tests) verarbeiten können. Surveyarbeiten ordnen LLM-Anwendungen nach Aufgabenklassen wie Codegenerierung, Codezusammenfassung, Fehlersuche, Testgenerierung oder Dokumentation @fan2023llmse @salem2024surveyllmse @llm4se2024slr @llm4se2024survey.
#heading(level: 3)[Training, Instruction Tuning und Prompting]
LLMs werden typischerweise in mehreren Phasen entwickelt. In einer Vortrainingsphase lernen Modelle aus großen Text- und Codekorpora statistische Regularitäten. Für den Einsatz als Assistenzsysteme werden Modelle häufig zusätzlich auf Anweisungen und Dialogformate ausgerichtet („instruction tuning“). Der GPT-4 Technical Report beschreibt diese Ausrichtung auf Systemebene und diskutiert Safety- und Evaluationsaspekte, ohne die vollständige Trainingspipeline offen zu legen @openai2023gpt4.
Im Engineering-Kontext ist der Prompt damit nicht nur Eingabe, sondern ein Steuerungsinstrument. Für diese Arbeit sind vor allem folgende Hebel relevant:
- **Aufgabenrahmen:** Ziel, gewünschtes Artefaktformat, Definition von Begriffen und Abgrenzung (z. B. „Requirement“ vs. „Designentscheidung“).
- **Kontextwahl:** Welche Code- und Textartefakte werden bereitgestellt, und welche Teile werden bewusst ausgeblendet, um Überinterpretation zu begrenzen?
- **Ausgabe-Constraints:** Belegpflicht, Kennzeichnung unsicherer Aussagen, deterministische Parameter (z. B. niedrige Temperatur), feste Templates.
Da LLMs ein begrenztes Kontextfenster besitzen, wird in Forschung und Praxis häufig Retrieval-Augmented Generation (RAG) eingesetzt: Relevante Textstellen werden zunächst über Suche/Retrieval ausgewählt und anschließend als Kontext in die Generierung eingebracht. #cite(<lewis2020rag>, form: "prose") beschreiben dieses Grundprinzip für wissensintensive Aufgaben. Für Requirements-Extraktion aus Legacy-Code ist RAG naheliegend, weil relevante Regeln, Konfigurationen und UI-Strings über große Repositories verteilt sind und eine „Alles in den Prompt“-Strategie nicht skaliert.
Prompting-Strategien wie Chain-of-Thought können die Qualität komplexer Ableitungen verbessern, bergen im Requirements-Kontext jedoch ein Risiko: Längere Begründungen können plausibel wirken und dadurch Fehlannahmen stabilisieren. #cite(<wei2022cot>, form: "prose") zeigen Chain-of-Thought als wirksame Prompttechnik; für diese Arbeit folgt daraus vor allem, dass Begründungen stets mit Artefaktbelegen gekoppelt werden müssen und nicht als eigenständige Evidenz gelten.
#heading(level: 3)[LLMs für Code: Spezialisierung, Stärken und Grenzen]
Neben allgemeinen Modellen existieren code-spezialisierte LLMs, die auf Codekorpora vortrainiert oder nachtrainiert wurden. Ein prominentes Beispiel ist Code Llama, dessen Technical Report Training und Evaluationsaufbau beschreibt und die Ausrichtung auf Codeaufgaben explizit macht @roziere2023codellama. Aus praktischer Sicht sind bei code-spezifischen Modellen typischerweise drei Stärken zu beobachten:
- **Syntaxnahe Mustererkennung:** Wiederkehrende Idiome, Framework-Patterns und typische Kontrollstrukturen werden zuverlässig erkannt.
- **Semantische Zusammenfassung:** Funktionen und Module lassen sich in natürliche Sprache übertragen, inklusive grober Zweckbeschreibung.
- **Transformation und Vorschläge:** Refactorings, Testideen oder API-Skizzen können generiert und iterativ verfeinert werden.
Den Stärken stehen systematische Grenzen gegenüber. LLMs „verstehen“ Code nicht im Sinne einer formalen Semantik. Sie approximieren Bedeutung über Muster aus Trainingsdaten und aus dem gegebenen Kontext. Insbesondere in Legacy-Systemen mit proprietären Frameworks, kundenspezifischen Erweiterungen und historisch gewachsenen Konventionen ist die Wahrscheinlichkeit hoch, dass Modelle plausible, aber falsche Erklärungen liefern. Genau diese Plausibilität ist im Requirements-Kontext kritisch, weil Text als Spezifikation eine höhere Autorität erhält als eine rein technische Zusammenfassung.
#heading(level: 3)[Halluzinationen und Verlässlichkeit: Relevanz für Requirements]
Halluzinationen bezeichnen Ausgaben, die syntaktisch korrekt und plausibel wirken, aber nicht durch Eingabedaten oder Weltwissen gedeckt sind. #cite(<ji2023hallucination>, form: "prose") liefern eine Taxonomie und diskutieren Detektions- und Mitigationsansätze. Für Requirements ist die Gefahr besonders kritisch, weil falsche Requirements nicht als „Bug“ im Text auffallen, sondern als scheinbar saubere Spezifikation in nachgelagerte Architektur- und Implementationsentscheidungen einfließen können.
Zusätzlich zu Halluzinationen sind zwei weitere Verlässlichkeitsthemen relevant:
- **Daten- und Domänenbias:** Modelle spiegeln Verteilungen und Annahmen aus Trainingsdaten wider. #cite(<bender2021stochastic>, form: "prose") diskutieren solche Risiken systematisch und betonen Governance-Fragen.
- **Reproduzierbarkeit:** Kleine Promptänderungen oder Parameterunterschiede können zu variierenden Ergebnissen führen. Für einen engineeringfähigen Prozess sind daher Steuerungsmechanismen (z. B. feste Templates, deterministische Einstellungen, versionierte Prompts) notwendig.
Für diese Arbeit folgt daraus, dass LLM-Ausgaben im Requirements-Kontext nicht als „Quelle“, sondern als Hypothesenmaterial zu behandeln sind. Erst durch Traceability (Belege) und Validierung (Expertenreview, Laufzeitchecks) wird aus einer Hypothese eine belastbare Anforderung.
#heading(level: 3)[LLMs im Requirements Engineering: Stand der Forschung]
Die Forschung zu LLMs im Requirements Engineering ist dynamisch und lässt sich sinnvoll in eine Vorgeschichte (NLP/IR-Ansätze) und in aktuelle LLM-spezifische Arbeiten gliedern. Vor dem breiten Aufkommen von LLMs wurden Aufgaben wie Terminologieextraktion, Klassifikation, Qualitätsprüfung und Traceability häufig mit Natural Language Processing (NLP) und Information Retrieval (IR) adressiert. #cite(<zhao2021nlpre>, form: "prose") geben einen Überblick über NLP-Verfahren im Requirements Engineering, inklusive typischer Problemklassen (z. B. Mehrdeutigkeit, Konsistenz, Vollständigkeit). Für Traceability ist die IR-basierte Link-Recovery-Literatur ein wichtiger Referenzpunkt, weil sie zeigt, welche Artefakte (Requirements, Code, Dokumentation) typischerweise verknüpft werden und welche Evaluationsmuster (Precision/Recall, Gold-Standards) sich etabliert haben @borg2013traceability.
Aktuelle Arbeiten zu LLMs im Requirements Engineering verschieben den Schwerpunkt. Während NLP/IR-Ansätze oft auf klar definierten Teilaufgaben mit begrenzten Ausgaben (Labels, Links, Hinweise) beruhen, können LLMs artefaktnahe Texte erzeugen, umformulieren und strukturieren. Dieser Übergang ist ambivalent: Einerseits entsteht ein direkter Produktivitätshebel, andererseits steigt das Risiko, dass sprachlich "gute" Texte als Spezifikation akzeptiert werden, obwohl die fachliche Basis unzureichend ist @ji2023hallucination @hemmat2025directions.
Systematische Übersichten ordnen die LLM-Nutzung im RE entlang klassischer Prozessschritte ein. #cite(<hemmat2025directions>, form: "prose") fassen Forschungsrichtungen zu LLMs im Software Requirements Engineering zusammen und nennen als wiederkehrende Problemfelder Qualitätssicherung, Nachvollziehbarkeit und Domänenabhängigkeit. Eine weitere Review zum ChatGPT-Einsatz im Requirements Engineering liefern #cite(<marques2024chatgptre>, form: "prose"). Sie diskutieren den Einsatz entlang typischer RE-Aktivitäten (Elicitation, Analyse, Spezifikation, Validierung) und heben als zentrale Herausforderungen inkonsistente Ergebnisse, begrenztes Domänenwissen sowie die Schwierigkeit belastbarer Evaluationen hervor.
In der Detailperspektive lassen sich aktuelle LLM-Arbeiten grob nach Anwendungsfeldern bündeln:
- **Strukturierung und (Re-)Formulierung von Requirements:** #cite(<norheim2024structuring>, form: "prose") untersuchen, wie LLMs naturalsprachliche Anforderungen in strukturiertere Formen überführen können. #cite(<okamoto2025restructuring>, form: "prose") adressieren die automatische Umstrukturierung von Software Requirements Specifications mit dem Ziel, Standardkonformität zu erhöhen.
- **Qualitätsunterstützung und Defektanalyse:** #cite(<fantechi2023inconsistency>, form: "prose") evaluieren ChatGPT für Inkonsistenzdetektion in naturalsprachlichen Requirements. #cite(<luitel2024completeness>, form: "prose") untersuchen LLM-gestützte Assistenz zur Verbesserung der Requirements-Vollständigkeit.
- **Elicitation und Stakeholder-Perspektiven:** #cite(<marczak2023humanvalue>, form: "prose") zeigen, wie LLMs zur Generierung wertorientierter User Stories als "Inspirationsimpulse" eingesetzt werden können. Diese Richtung ist für Reverse Requirements Engineering indirekt relevant, weil sie zeigt, wie LLMs fehlende Stakeholder-Sichten ergänzen können, ohne den Code als Primärbeleg zu ersetzen.
- **Domänenspezifische Requirements (Safety/Compliance):** #cite(<nouri2024safety>, form: "prose") betrachten LLMs bei der Engineering-Unterstützung von Safety Requirements im Kontext autonomen Fahrens. #cite(<hassani2024legal>, form: "prose") diskutiert LLM-Einsatz für rechtliche Compliance- und Regulationsanalyse. Solche Arbeiten verdeutlichen, dass LLMs nicht nur Text umformulieren, sondern auch Wissensstrukturen (Normen, Regeln) operationalisieren sollen; zugleich erhöhen sich die Anforderungen an Belegbarkeit und Haftung.
Über die einzelnen Studien hinaus ist der Evidenzstand derzeit heterogen. Viele Arbeiten sind als Workshopbeiträge oder "preliminary evaluations" angelegt, nutzen begrenzte Datensätze und kombinieren automatische Metriken mit Expertenurteilen. Zudem sind Prompting-Strategien, Modellversionen und Kontexteinstellungen häufig nicht vollständig standardisiert, was die Reproduzierbarkeit erschwert @fan2023llmse @hemmat2025directions. Aus Sicht dieser Arbeit folgt daraus eine klare Konsequenz: LLMs sind im Requirements Engineering am stärksten als Assistenzsysteme in einem kontrollierten Prozess, in dem (1) Aussagen mit Belegen verknüpft werden, (2) Unsicherheit explizit markiert wird und (3) fachliche Validierung als definierter Kontrollpunkt erfolgt.
Für Reverse Requirements Engineering lässt sich der Nutzen damit präzisieren: LLMs können Kandidaten-Requirements aus großen Artefaktmengen (Code, Kommentare, UI-Strings, Konfiguration) verdichten und in eine konsistente Spezifikationsform überführen. Die fachliche Belastbarkeit entsteht jedoch erst durch Traceability zu Codebelegen und die Validierung durch Experten, insbesondere bei Safety-, Compliance- und Abrechnungslogik.
#heading(level: 3)[Absicherung: Human-in-the-loop, Belege und Prozesskontrollen]
Die Literatur legt nahe, dass LLMs im Software Engineering dann robust eingesetzt werden können, wenn sie in einen Prozess eingebettet sind, der Fehler systematisch begrenzt @fan2023llmse @hemmat2025directions. Für die Requirements-Extraktion aus Legacy-Code sind folgende Kontrollen praxisnah:
- **Belegpflicht (Evidence-First):** Jedes generierte Requirement erhält mindestens einen konkreten Beleg (Datei/Komponente/Query/GUI-String) sowie eine kurze Begründung, warum der Beleg die Aussage trägt.
- **Trennung von Fakt und Interpretation:** Technische Fakten (z. B. „Status = 'Closed' verhindert Update“) werden getrennt von fachlicher Interpretation (z. B. „Abgeschlossene Aufträge sind schreibgeschützt“) dokumentiert.
- **Mehrstufige Validierung:** Automatische Checks (z. B. Linting auf Verbformen, Konsistenzregeln) werden mit Expertenreview kombiniert.
- **Reproduzierbarkeit:** Versionierung von Promptvorlagen, Modellversionen und Kontextzuschnitten, um Ergebnisse vergleichbar zu machen.
Diese Kontrollen adressieren nicht alle Risiken, reduzieren aber die typischen Fehlerklassen (Halluzination, Überinterpretation, fehlende Konsistenz) und schaffen die Grundlage für eine belastbare Evaluation in Kapitel 6.
#heading(level: 3)[Qualitätsbewertung und Messgrößen im Requirements-Kontext]
Die Qualität von LLM-Ergebnissen wird in vielen Arbeiten über allgemeine Textmetriken oder Task-spezifische Benchmarks bewertet. Für Requirements-Extraktion aus Code sind solche Metriken nur begrenzt aussagekräftig, weil der zentrale Anspruch nicht „sprachliche Ähnlichkeit“, sondern fachliche Korrektheit, Prüfbarkeit und Nachvollziehbarkeit ist @hemmat2025directions @marques2024chatgptre. Eine zweckmäßige Qualitätsbewertung sollte daher an RE-Kriterien anschließen und explizit zwischen drei Ebenen unterscheiden:
- **Statement-Qualität:** Ist ein Requirement eindeutig, vollständig im Satzbau, frei von nicht belegten Annahmen und mit Akzeptanzkriterium bzw. Prüfidee versehen?
- **Set-Qualität:** Ist die Menge der Requirements konsistent, nicht redundant und deckt die relevanten Prozesse und Varianten ab, ohne sich in Detailfällen zu verlieren?
- **Traceability-Qualität:** Sind Belege reproduzierbar auffindbar (z. B. Dateipfad, Methode, SQL-Query), und lässt sich die Ableitung von „Beleg → Requirement“ nachvollziehen?
Für Legacy-Migrationen ist zudem die Fehlerkostenperspektive entscheidend. Ein fehlendes Requirement kann zu Funktionsverlust führen, ein falsches Requirement kann zu fehlerhaften Designentscheidungen führen, und ein unpräzises Requirement verursacht Review- und Nacharbeit. Daraus folgt eine pragmatische Bewertung: Requirements mit hoher Migrationskritikalität (z. B. Sicherheitsregeln, Abrechnungslogik, Berechtigungen) sollten strengere Evidenzanforderungen und intensivere Reviews erhalten als periphere Funktionen. Dieses Prinzip ist kompatibel mit der risikobasierten Priorisierung von Qualitätsanforderungen @glinz2008quality und lässt sich auf Funktionsanforderungen übertragen.
#heading(level: 3)[Zwischenfazit zu 2.2]
LLMs liefern im Software Engineering eine leistungsfähige Assistenz für Analyse, Zusammenfassung und Textproduktion, sind jedoch nicht verlässlich im Sinne formaler Korrektheit @fan2023llmse @ji2023hallucination. Für Requirements ist der entscheidende Punkt, dass die Qualität nicht an der sprachlichen Glätte, sondern an Nachvollziehbarkeit und Prüfbarkeit hängt. Daraus folgt für diese Arbeit ein designierter "Sicherheitsgurt": Evidence-First, Traceability und Human-in-the-loop sind keine Zusatzoptionen, sondern Kernelemente des Vorgehens.
#heading(level: 2)[Legacy-Modernisierung und Stand der Forschung]
#heading(level: 3)[Charakteristika von Legacy-Systemen]
Legacy-Systeme sind nicht allein durch ihr Alter definiert, sondern durch ihren Kontext: Sie tragen geschäftskritische Funktionen, sind über lange Zeit erweitert worden und weisen oft starke technische und organisatorische Abhängigkeiten auf. #cite(<bisbal1999legacy>, form: "prose") beschreiben typische Merkmale wie enge Kopplung, heterogene Technologien, schwer austauschbare Komponenten und unzureichende Dokumentation. Gerade letzteres ist für Modernisierungsvorhaben problematisch, weil Entscheidungen ohne belastbare Anforderungsbasis zu Funktionsverlusten und Akzeptanzproblemen führen können.
Im ERP-Kontext verschärfen sich diese Merkmale häufig durch:
- **Domänenkomplexität:** Geschäftsregeln sind zahlreich, variantenreich und teilweise kundenspezifisch.
- **Datenzentrierung:** Prozesse hängen stark von Datenmodellen, Stammdatenqualität und historisch gewachsenen Datenkonventionen ab.
- **Integrationslast:** Schnittstellen zu Drittsystemen (z. B. Buchhaltung, Shop, Dokumentenmanagement) sind über Jahre organisch entstanden.
Damit wird nachvollziehbar, warum Requirements-Extraktion aus der Codebasis nicht nur ein Dokumentationsprojekt, sondern ein Risikoreduktionsinstrument für Migrationen ist.
#heading(level: 3)[Modernisierungsstrategien und Reengineering als Prozess]
Modernisierung kann unterschiedliche Strategien annehmen, von "Lift-and-Shift" bis zur vollständigen Neuimplementierung. Die Literatur betont wiederholt, dass die Wahl einer Strategie von Risiko, Zielarchitektur und verfügbaren Ressourcen abhängt und daher explizit geplant werden sollte @sneed1995planning. Eine zentrale Aussage ist dabei, dass Reengineering nicht als rein technischer Umbau verstanden werden kann: Ohne fachliche Leitplanken entstehen technische Verbesserungen, die am Bedarf vorbeilaufen oder bestehende Fachlogik unabsichtlich verändern.
Aus Sicht dieser Arbeit lassen sich Modernisierungsstrategien pragmatisch entlang zweier Achsen einordnen: (1) Wie stark wird die bestehende Implementierung weitergenutzt? (2) Wie stark wird die Zielarchitektur verändert? Daraus ergeben sich typische Strategietypen, die in der Praxis auch kombiniert auftreten:
- **Weiterbetrieb mit Hülle (Wrapping):** Die Legacy-Logik bleibt bestehen, wird aber über neue Schnittstellen oder UI-Schichten zugänglich gemacht. Vorteil ist geringe Eingriffstiefe; Nachteil ist, dass technische Schulden und Engpässe erhalten bleiben.
- **Schrittweise Modularisierung:** Teile der Legacy-Anwendung werden sukzessive in neue Komponenten überführt, während andere Teile weiterlaufen. Vorteil ist Risikostreuung und frühe Nutzenrealisierung; Nachteil ist erhöhte Integrationskomplexität während der Übergangsphase.
- **Reengineering/Refactoring:** Die bestehende Logik wird strukturell überarbeitet (z. B. Entkopplung, Schichten, bessere Testbarkeit), ohne den Funktionsumfang grundsätzlich zu verändern. Vorteil ist bessere Wartbarkeit; Nachteil ist hoher Analyseaufwand, gerade ohne Requirementsbasis.
- **Neuimplementierung mit Funktionsparität:** Die Legacy-Logik wird auf neuer Technologie nachgebaut, häufig mit dem Anspruch, zunächst funktional äquivalent zu sein. Vorteil ist saubere Zielarchitektur; Nachteil ist die hohe Abhängigkeit von vollständigen, korrekten Requirements.
Für ERP-Systeme ist die Wahl einer Strategie stark datengetrieben. Datenmodelle, Schnittstellenverträge und Buchungslogik definieren die „harten Kanten“ einer Migration. Damit steigt der Stellenwert von Requirements, die Datenobjekte, Zustandsmodelle und Integrationspunkte explizit machen. Besonders migrationskritisch sind dabei Anforderungen, die in der Legacy-Implementierung als implizite Konvention existieren (z. B. Statuscodes, historische Sonderfälle, kundenspezifische Maskenlogik), weil sie ohne gezielte Extraktion und Validierung leicht verloren gehen.
#cite(<bisbal1997overview>, form: "prose") geben einen Überblick über Migrationsansätze und ordnen typische Risikofelder ein, darunter Datenmigration, Funktionsäquivalenz und organisatorische Abhängigkeiten. #cite(<wu1997toolkit>, form: "prose") argumentieren ergänzend, dass Werkzeugunterstützung nur dann wirksam ist, wenn sie in eine methodische Kette eingebettet ist. Diese Argumentation ist direkt anschlussfähig an KI-gestützte Verfahren: Auch LLM-basierte Automatisierung entfaltet Nutzen nur innerhalb eines reproduzierbaren Prozesses mit klaren Kontrollpunkten.
#heading(level: 3)[Zielarchitekturen: Web, Cloud und „Cloud-native“]
Die Modernisierung vieler Legacy-Anwendungen zielt auf webbasierte, plattformunabhängige Oberflächen und auf Betriebsmodelle, die Skalierung, automatisiertes Deployment und schnelle Iteration unterstützen. #cite(<kratzke2017cloudnative>, form: "prose") fassen in einer systematischen Mapping Study zusammen, welche Merkmale cloud-nativer Anwendungen in der Forschung und Praxis wiederkehren. Dazu zählen typischerweise automatisierte Bereitstellung, resiliente Komponenten, horizontale Skalierung und eine stärkere Trennung von Build- und Run-Umgebungen.
Im selben Zielraum werden Microservices häufig als Architekturstil diskutiert. #cite(<pahl2016microservices>, form: "prose") kartieren Forschung zu Microservices und zeigen wiederkehrende Problemfelder, unter anderem die Wahl der richtigen Servicegranularität, die erhöhte Komplexität im Betrieb und Anforderungen an Observability. Für Migrationsprojekte ist daraus eine pragmatische Schlussfolgerung ableitbar: Modularisierung ist ein Ziel, erzeugt aber zugleich neue Anforderungen (z. B. Deployment-Pipelines, Monitoring, Sicherheitskonzepte), die im Requirements-Set sichtbar sein müssen.
Für die Requirementsarbeit bedeutet die Zielarchitekturverschiebung eine Verschiebung des Schwerpunktes: Während in klassischen Client/Server-Architekturen die fachliche Funktionslogik oft dominiert, rücken in Web- und Cloud-Kontexten betriebliche und sicherheitsbezogene Qualitätsmerkmale stärker in den Vordergrund. ISO/IEC 25010:2011 bietet hierfür eine hilfreiche Taxonomie @iso25010_2011. Für Modernisierungsvorhaben lassen sich vor allem folgende Qualitätsmerkmale als wiederkehrend beobachten:
- **Sicherheit:** Identitäten, Rollenmodelle, Mandantenfähigkeit, Auditierbarkeit.
- **Zuverlässigkeit:** Fehlerresistenz, Wiederanlauf, Degradationsverhalten.
- **Performance-Effizienz:** Antwortzeiten, Lastverhalten, Skalierungsgrenzen.
- **Wartbarkeit:** Änderbarkeit, Testbarkeit, Modularität und technische Schuld.
- **Kompatibilität und Interoperabilität:** Schnittstellenstabilität, Integrationsfähigkeit mit Drittsystemen.
Diese Merkmale sind nicht neu, ihre Sichtbarkeit im Projekt nimmt jedoch zu, weil Cloud- und Webbetrieb ein engeres Zusammenspiel von Entwicklung und Betrieb erzwingt. Für Reverse Requirements Engineering folgt daraus, dass der Blick auf die Legacy-Codebasis systematisch um Betriebs- und Sicherheitsanforderungen ergänzt werden muss, auch wenn diese im Code nur indirekt sichtbar sind (z. B. über Deployment-Skripte, Konfigurationen, Logging-Policies oder Rechteprüfungen).
Sicherheitsanforderungen werden in Cloud-Migrationskontexten häufig unterschätzt. Eine systematische Mapping Study zu Security-Aspekten bei Legacy-to-Cloud-Migrationen @security2014legacycloud zeigt, dass Identitätsmanagement, Datenflusskontrolle und Compliance wiederkehrende Kernprobleme sind. Für diese Arbeit bedeutet dies, dass Requirements-Extraktion aus Code um Sicherheits- und Datenschutzanforderungen ergänzt werden muss, da sie nicht in jedem Quellcodefragment explizit sichtbar sind.
#heading(level: 3)[Stand der Forschung: KI-Unterstützung in Modernisierungsvorhaben]
Die Forschung zu KI- bzw. LLM-Unterstützung im Modernisierungskontext ist im Vergleich zu klassischen Reengineering-Ansätzen jünger. Die Übersichten zu LLM4SE @fan2023llmse @llm4se2024slr zeigen, dass ein Teil der Arbeiten auf Codeverständnis, Dokumentation und Artefakttransformation zielt. Spezifisch für Requirements Engineering bündeln Reviews und SLRs erste Evidenz und Forschungsrichtungen @marques2024chatgptre @hemmat2025directions.
Aus dieser Literatur lassen sich zwei robuste Aussagen ableiten:
- **LLMs sind besonders stark in der Strukturierung und sprachlichen Formulierung**, also dort, wo aus fragmentierten Hinweisen ein konsistenter Text entstehen muss.
- **LLMs benötigen technische und organisatorische Sicherungen**, wenn Ergebnisse als Entscheidungsgrundlage in Migrationen dienen sollen (z. B. Belege, Review, reproduzierbarer Prozess).
Damit ist eine zentrale Motivation dieser Arbeit begründet: Eine Legacy-Modernisierung benötigt belastbare Requirements, die im Legacy-Kontext oft fehlen. LLMs sind als Assistenz zur Rekonstruktion naheliegend, müssen jedoch methodisch so eingesetzt werden, dass Verlässlichkeit und Nachvollziehbarkeit systematisch erhöht werden.
#heading(level: 3)[Zwischenfazit zu 2.3]
Legacy-Modernisierung ist ein sozio-technisches Vorhaben, das technische Umbauten und fachliche Zielsetzungen integrieren muss @bisbal1999legacy @sneed1995planning. Moderne Zielarchitekturen (Web/Cloud) verschieben zudem die Anforderungslandschaft, weil Betriebs- und Sicherheitsanforderungen stärker in den Vordergrund treten @kratzke2017cloudnative @security2014legacycloud. Für die vorliegende Arbeit folgt daraus, dass Requirements-Extraktion nicht nur der Funktionsrekonstruktion dient, sondern die Grundlage für Migrationsentscheidungen, Priorisierung und Qualitätssicherung bildet.
#heading(level: 3)[Kapitelzusammenfassung und Anschluss]
Die drei Themenblöcke dieses Kapitels greifen ineinander. Requirements Engineering liefert Kriterien, um Anforderungen prüfbar und nachvollziehbar zu formulieren @iso29148_2018. Reverse Requirements Engineering überträgt diese Kriterien in einen Kontext, in dem Anforderungen aus bestehenden Artefakten rekonstruiert werden müssen @chikofsky1990taxonomy @yu2005retr. Large Language Models können diese Rekonstruktion unterstützen, sind aber fehleranfällig und benötigen Prozesskontrollen, vor allem gegen Halluzinationen und Überinterpretation @ji2023hallucination @fan2023llmse. Legacy-Modernisierung schließlich liefert die praktische Motivation und zeigt, warum eine belastbare Anforderungsbasis migrationskritisch ist @bisbal1999legacy @sneed1995planning.
Damit ist das Fundament gelegt, um in Kapitel 3 den konkreten Fallkontext zu beschreiben und in Kapitel 4 ein Vorgehensmodell zu entwickeln, das KI-Unterstützung, Traceability und Validierung systematisch miteinander verbindet.
#include "02_theoretischer_hintergrund/02_01_requirements_engineering.typ"
#pagebreak()
#include "02_theoretischer_hintergrund/02_02_large_language_models.typ"
#pagebreak()
#include "02_theoretischer_hintergrund/02_03_legacy_modernisierung.typ"
#pagebreak()
@@ -0,0 +1,104 @@
#heading(level: 2)[Requirements Engineering und Reverse Requirements Engineering]
#heading(level: 3)[Begriff und Zielsetzung des Requirements Engineering]
Der Begriff Requirements Engineering (RE) umfasst die systematische Erhebung, Analyse, Spezifikation, Validierung und Verwaltung von Anforderungen an ein System über dessen Lebenszyklus. In Standards @iso29148_2018 @ieee830_1998 wird Requirements Engineering als eigenständiger Prozess verstanden, der sowohl fachliche Ziele (z. B. unterstützte Geschäftsprozesse) als auch technische und organisatorische Randbedingungen (z. B. Sicherheitsvorgaben, Betriebsmodelle) in überprüfbare Aussagen überführt.
Im Kern adressiert das Requirements Engineering zwei Themen:
/ Kommunikation zwischen Domäne und Technik: Anforderungen müssen fachlich verständlich und gleichzeitig so präzise sein, dass sich daraus eine Software-Architektur ableiten lässt, die implementiert, getestet und geändert werden kann.
/ Umgang mit Unsicherheit und Wandel: Anforderungen sind zu Projektbeginn selten vollständig. Requirements Engineering ist daher nicht nur Dokumentation, sondern auch ein iterativer Klärungs- und Abstimmungsprozess.
\
Ein etablierter Ansatz zur Strukturierung von diversen Sichtweisen ist das Viewpoint-Konzept @kotonya1996viewpoints, bei dem Anforderungen aus unterschiedlichen Perspektiven modelliert und anschließend konsolidiert werden.
_Für diese Arbeit ist die Perspektivenorientierung relevant, weil implementierter Code typischerweise keine expliziten Stakeholder-Sichten enthält. Für eine Migration auf Basis eines Reverse-Engineering-Ansatzes sind diese aber relevant für die Implementierung und architekturelle Entscheidungen (z. B. Nutzerrollen, kundenspezifische Varianten, regulatorische Vorgaben)._
#heading(level: 3)[Arten von Requirements und Qualitätskriterien]
In der Literatur wird häufig zwischen funktionalen Anforderungen (Was soll das System tun?) und Qualitäts- bzw. nicht-funktionalen Anforderungen (Welche Eigenschaften und Randbedingungen gelten?) unterschieden. Die Praxis zeigt jedoch, dass diese Trennung nicht immer scharf ist. Eigenschaften können sowohl als Systemverhalten (z. B. „Audit-Log erzeugen") als auch als Qualitätsziel (z. B. „Nachvollziehbarkeit") formuliert werden @glinz2007nfr. Für das Reverse Requirements Engineering ist diese Unschärfe besonders relevant, weil Quellcode meist Verhalten konkretisiert, Qualitätsziele aber häufig implizit bleiben (z. B. Performance-Workarounds, Sicherheitsannahmen).
Für die Qualität einzelner Requirements gibt es etablierte Standards. @iso29148_2018 nennt unter anderem Eindeutigkeit, Konsistenz, Vollständigkeit, Verifizierbarkeit und Nachvollziehbarkeit als zentrale Eigenschaften. @ieee830_1998 formuliert ähnliche Prinzipien für Software Requirements Specifications, mit stärkerem Fokus auf Dokumentstruktur und Lesbarkeit.
Für die Bewertung von KI-extrahierten Requirements sind drei Kriterien maßgeblich relevant:
/ Verifizierbarkeit: Ein Requirement ist so formuliert, dass ein Test oder eine Prüfmethode ableitbar ist (z. B. Messkriterium, Akzeptanzbedingung).
/ Eindeutigkeit: Formulierungen vermeiden Mehrdeutigkeiten und definieren Begriffe, die in der Domäne unterschiedlich interpretiert werden können \ (z.B. „Das System soll Aufträge schnell verarbeiten" vs. „Das System soll einen Auftrag innerhalb von 2 Sekunden validieren und bestätigen")
/ Nachvollziehbarkeit (Traceability): Es ist erkennbar, aus welchem Requirement das Artefakt (Code, Konfiguration, Datenbank, Ticket, Interview) abgeleitet wurde.
Nicht funktionale Anforderungen (z.B. Qualitätsanforderungen) bedürfen einer besonderen Betrachtung, weil sie über die reine Funktionsgleichheit hinaus die Zielarchitektur bestimmen. #cite(<glinz2008quality>, form: "prose") argumentiert, dass Qualitätsanforderungen risikobasiert und wertorientiert priorisiert werden sollten. Für Legacy-Migrationen ist dies nachvollziehbar: Ein „vollständiges" Requirements-Set ist praktisch schwer erreichbar, gleichzeitig sind bestimmte Non-Functional Requirements (z. B. Datenschutz, Verfügbarkeit, Rollout-Fähigkeit) hochkritisch, weil sie Architekturentscheidungen dominieren.
Für die inhaltliche Strukturierung von Qualitätsanforderungen ist das Qualitätsmodell ISO/IEC 25010:2011 verbreitet, das Qualitätsmerkmale wie Performance-Effizienz, Zuverlässigkeit, Sicherheit oder Wartbarkeit systematisch ordnet. Für Reverse Requirements Engineering ist dies hilfreich, weil aus Code häufig nur Teilaspekte sichtbar werden (z. B. Caching-Mechanismen als Hinweis auf Performance-Annahmen), während andere Qualitätsziele (z. B. „Maintainability") eher indirekt über Architekturentscheidungen und Entwicklungspraktiken wirksam werden @iso25010_2011.
Die Relevanz saubere formulierter Requirements zeigt sich auch in der Risikoperspektive. #cite(<lawrence2001toprisk>, form: "prose") beschreiben Requirements Engineering als primäres Risiko, wenn Anforderungen unklar, instabil oder unvollständig sind.
\ \
_Für diese Arbeit folgt daraus, dass KI-gestütztes Reverse-Requirements-Engineering nicht nur „mehr Text" erzeugen darf, sondern gezielt die Risiken der Unklarheit und der Fehlinterpretation reduzieren muss._
#heading(level: 3)[Spezifikationsformen und Grad der Formalisierung]
Requirements werden in unterschiedlichen Repräsentationsformen dokumentiert. Standards wie IEEE 830-1998 und ISO/IEC/IEEE 29148:2018 fokussieren auf strukturierte Spezifikationen (z. B. SRS) und definieren typische Kapitel (Zweck, Systemkontext, funktionale Anforderungen, Schnittstellen, Qualitätsanforderungen, Annahmen). Zudem existieren weniger formale Formen wie User Stories, Use-Case-Beschreibungen oder Backlog-Einträge, die in agilen Settings Verwendung finden @ieee830_1998 @iso29148_2018.
Für Reverse Requirements Engineering sind zwei Punkte entscheidend:
- *Form* beeinflusst Interpretierbarkeit: Eine kurze User Story („Als Nutzer möchte ich …") ist leicht verständlich, transportiert aber weniger Randbedingungen, Datenregeln oder Fehlerfälle. Eine SRS-Formulierung kann präziser sein, erfordert aber mehr Kontext und Definitionen.
- *Grad* der Formalisierung beeinflusst Prüfbarkeit: Je stärker Requirements mit Akzeptanzkriterien, Beispielen oder Messgrößen verknüpft sind, desto einfacher sind Reviews und Tests. #cite(<pohl2010re>, form: "prose") betont Anforderungen-Validierung als eigene Disziplin.
_Für diese Arbeit wird daher ein hybrider Stil gewählt: Requirements werden als kurze, klare Soll-Aussagen formuliert und jeweils um Kontext (Akteur/Prozess), Randbedingungen (Vorbedingungen, Datenobjekte) und mindestens eine Prüfidee ergänzt._
#heading(level: 3)[Traceability als Verbindung zwischen Code und Requirement]
Traceability bezeichnet die Verknüpfung von Requirements und Artefakten, wie z.B. Code oder Test. #cite(<gotel1994traceability>, form: "prose") bezeichnen Traceability als wiederkehrendes Problem, vor allem bei unterschiedlichen Arten von Artefakten.
Beim Reverse Requirements Engineering ist Traceability nicht nur ein „Nice-to-have", sondern eine Grundvoraussetzung.\
Ein Requirement lässt sich gegen konkrete Codeausschnitte oder Laufzeitbeobachtungen prüfen, da das Requirement ja aus dem Code selbst entsteht.
In Legacy-Systemen ist die ursprüngliche Traceability typischerweise unvollständig, falls überhaupt vorhanden: Hinweise finden sich in Commit-Messages, Branch-Namen, Datenbankskripten, Konfigurationsdateien, UI-Texten oder in impliziten Konventionen. Der Anspruch dieser Arbeit besteht daher nicht darin, „perfekte" Traceability wiederherzustellen, sondern eine minimal belastbare, reproduzierbare Verknüpfung zwischen extrahierten Requirements und Artefakten zu etablieren.
#heading(level: 3)[Abgrenzung von Reverse Engineering zu Reverse Requirements Engineering]
Reverse Engineering wird klassisch als Analyseprozess verstanden, der aus einem bestehenden System Wissen über Struktur, Verhalten und Designentscheidungen rekonstruiert. #cite(<chikofsky1990taxonomy>, form: "prose") prägen hierfür die Benennung und grenzen Reverse Engineering von Reengineering sowie Design Recovery ab. Für Requirements-nahe Fragestellungen ist hier relevant, dass Reverse Engineering nicht automatisch auch Requirements liefert, sondern erstmal nur technische Fakten (z. B. Abhängigkeiten, Datenflüsse, Zustandsautomaten).
Reverse Requirements Engineering (RRE) fokussiert sich dagegen auf die rückwärtsgerichtete Gewinnung von Requirements aus bestehenden Artefakten. Dabei kann das Ziel unterschiedlich interpretiert werden:
/ Rekonstruktion eines Soll-Zustands: Welche fachlichen Anforderungen werden durch die aktuelle Implementierung implizit erfüllt? Was war das ursprüngliche Ziel der Implementierung?
/ Rekonstruktion eines Ist-Zustands: Welche Funktionen und Regeln sind dagegen tatsächlich implementiert?
Gerade im Legacy-Umfeld ist diese Unterscheidung entscheidend. Die Codebasis enthält oft historisch entstandene Workarounds oder kundenspezifische Anpassungen. Ohne zusätzliche Validierung besteht das Risiko, dass RRE den Ist-Zustand als Soll-Zustand fehlinterpretiert.
Frühe Ansätze zur Brücke zwischen Reverse Engineering und Requirements liefern beispielsweise #cite(<yu2005retr>, form: "prose") mit „RETR: Reverse Engineering to Requirements". Der Beitrag betont, dass Requirements-Rückgewinnung eine methodische Kette aus Artefaktsichtung, Strukturierung und Validierung benötigt.
Methodisch lassen sich dabei grob zwei Analysestränge unterscheiden:
/ Statische Analyse: Ableitung von Struktur- und Datenflussinformationen aus Code und Artefakten ohne Ausführung (z. B. Abhängigkeiten, SQL-Statements, Aufrufketten). Statische Analyse skaliert gut, erkennt aber nicht zuverlässig Laufzeitbedingungen (z. B. Feature Flags, Konfigurationsvarianten).
/ Dynamische Analyse: Beobachtung von Laufzeitverhalten durch Logging, Tracing oder instrumentierte Tests (z. B. welche Regeln bei bestimmten Eingaben greifen). Dynamische Analyse ist näher am realen Verhalten, benötigt aber reproduzierbare Szenarien und Testdaten.
Reverse Requirements Engineering in einem Migrationsprojekt profitiert typischerweise von einer Kombination beider Stränge. Ohne dynamische Belege steigt das Risiko, dass nicht offensichtliche Bedingungen wie kundenspezifische Schalter übersehen werden. Ohne statische Analyse bleibt die Abdeckung häufig zu gering.
_Eine vollständige dynamische Analyse durch ein LLM ist mit den gegebenen technischen Möglichkeiten derzeit nicht praktikabel. Diese Arbeit fokussiert sich daher auf die statische Analyse von Artefakten und ergänzt sie um manuell erstellte Laufzeit-Artefakte wie Screenshots. Mit einem MCP-Server zur GUI-Beobachtung ist darüber hinaus eine teilweise dynamische Analyse möglich. Dieser Ansatz wird im Versuchsaufbau optional vorgesehen._
#heading(level: 3)[Typische Methodenkette für Requirements-Rückgewinnung aus Code]
Aus Sicht dieser Arbeit lässt sich Reverse Requirements Engineering einer Legacy-Codebasis als wiederholbarer Ablauf darstellen. Die konkrete Implementierung hängt vom System und den verfügbaren Artefakten ab, die grundlegenden Schritte sind jedoch weitgehend stabil:
1. *Scope und Domänenabgrenzung:* Auswahl relevanter Module, Datenobjekte und Prozesse (z. B. Auftragsabwicklung, Fakturierung).
2. *Artefakterhebung:* Quellcode, Konfiguration, UI-Texte, Datenbankschemata, Schnittstellenbeschreibungen, Change-Historie.
3. *Technische Analyse:* Struktur- und Abhängigkeitsanalyse, Identifikation von Kernkomponenten, Regeln und Integrationspunkten.
4. *Semantische Interpretation:* Ableitung fachlicher Aussagen aus technischen Implementierungen (z. B. Statusübergänge, Berechtigungsprüfungen).
5. *Formalisierung als Requirements:* Überführung in klare, testbare Anforderungen mit Kontext (Akteur, Vorbedingung, Ergebnis).
6. *Traceability-Anreicherung:* Verknüpfung jedes Requirements mit Belegen (Datei, Klasse, Methode, SQL-Statement, UI-String).
7. *Validierung:* Review durch Fachexperten und Abgleich mit Laufzeitverhalten, Tickets oder Kundenwissen.
In der Praxis unterscheiden sich Artefakte darin, wie direkt sie fachliche Aussagen stützen. Quellcode, der eine Regel hart erzwingt (z. B. „Update nur bei Status X"), ist als Beleg stärker als Kommentare oder UI-Texte, die lediglich Absichten ausdrücken. Für eine belastbare Requirementsbasis ist es daher sinnvoll, Belege zu klassifizieren und die Aussagekraft zu kennzeichnen, beispielsweise:
/ Primärbelege: Durchgesetzte Regeln im Code oder in Datenbankconstraints (z. B. Statusmaschinen, Validierungslogik, Berechtigungschecks).
/ Sekundärbelege: Indirekte Hinweise wie UI-Labels, Fehlermeldungen, Report-Layouts, Mappingtabellen oder Konfigurationsschalter.
/ Kontextbelege: Ticketbeschreibungen, Commit-Messages oder Interviewaussagen, die Motivation und Ausnahmen erklären, aber nicht zwingend im Code sichtbar sind.
Diese Einteilung dient der Risikobewertung: Requirements, die überwiegend auf Sekundär- oder Kontextbelegen beruhen, sind anfälliger für Fehlinterpretation und sollten priorisiert validiert werden. Datenbankschemata und SQL-Statements sind häufig besonders aussagekräftig, weil sie Domänenobjekte, Kardinalitäten und Geschäftsregeln (z. B. referentielle Integrität, historisierte Tabellen) abbilden.
_Für diese Arbeit werden in der Schrittkette lediglich Punkte 1 und 7 manuell durchgeführt, während die Schritte 2-6 durch KI-gestützte Analyse automatisiert werden sollen._
/*
#heading(level: 3)[Zwischenfazit zu 2.1]
Requirements Engineering liefert Kriterien und Artefaktformen, um Anforderungen präzise, prüfbar und nachvollziehbar zu beschreiben @iso29148_2018 @ieee830_1998. Reverse Requirements Engineering überträgt diese Zielsetzung in einen Kontext, in dem Requirements nicht vorliegen, sondern aus technischen Artefakten rekonstruiert werden. Für die vorliegende Arbeit folgt daraus, dass Automatisierung (z. B. durch KI) nur dann praktikabel ist, wenn Traceability und Validierung als feste Prozessbestandteile mitgeführt werden.
*/
@@ -0,0 +1,272 @@
#import "@preview/cetz:0.4.2"
#import "@preview/cetz-plot:0.1.3": plot
#set text(lang: "de")
#heading(level: 2)[Large Language Models im Software Engineering]
#heading(level: 3)[Künstliche Intelligenz, Machine Learning und Einordnung von LLMs]
Die Einordnung von Large Language Models folgt einer hierarchischen Begriffsstruktur, die sich in vier Ebenen gliedern lässt (vgl. @abb_hierarchie_ki):
*Künstliche Intelligenz (KI)* ist der Oberbegriff für Verfahren, die Aufgaben bearbeiten, welche in der Praxis typischerweise kognitive Fähigkeiten erfordern (z. B. Klassifikation, Planung, Sprachverarbeitung) @bishop2006prml.
*Machine Learning (ML)* ist ein Teilgebiet der KI, das Modelle aus Daten lernt, anstatt Regeln vollständig manuell zu spezifizieren (sogennante Expertensysteme). in der Praxis wird zwischen Systemen mit überwachtem Lernen (mit Zielwerten), unüberwachtem Lernen (ohne Zielwerte, System erkennt selbst eine Struktur) und Reinforcement Learning (Lernen über Rückmeldung, z.B. Ergebnis Richtig / Falsch) unterschieden @bishop2006prml @goodfellow2016dl. In der heutigen Welt sind Neuronale Netze die dominierende ML-Architektur, insbesondere für unstrukturierte Daten wie Text und Code.
*Deep Learning* ist wiederum ein Teilbereich von ML, der neuronale Netze mit vielen Parametern und vielen (tiefen) Verarbeitungsebenen nutzt, um geeignete Repräsentationen aus Rohdaten zu lernen. Charakteristisch ist, dass Merkmalsextraktion und Modellanpassung gemeinsam über Optimierung (typischerweise Gradientenverfahren) erfolgen.
*Large Language Models (LLMs)* sind eine spezielle Ausprägung von Neuronalen Netzen, die auf sehr großen Text- und Codemengen vortrainiert werden. Zudem verfügen sie über sehr große Mengen (>Milliarden) von Eingabeparametern (Tokens) und viele Schichten (>100). In der aktuellen Modellgeneration dominiert die Transformer-Architektur @vaswani2017attention, deren Funktionsweise in den folgenden Abschnitten erläutert wird.
#figure(
cetz.canvas({
import cetz.draw: *
rect((-6, -2.8), (6, 2.8), stroke: black + 0.6pt, fill: luma(242))
content((0, 2.35), text(size: 9pt, weight: "bold")[Künstliche Intelligenz (KI)])
rect((-5.4, -2.2), (5.4, 1.9), stroke: black + 0.6pt, fill: luma(228))
content((0, 1.45), text(size: 9pt, weight: "bold")[Machine Learning (ML)])
rect((-4.8, -1.6), (4.8, 1.0), stroke: black + 0.6pt, fill: luma(214))
content((0, 0.55), text(size: 9pt, weight: "bold")[Deep Learning])
rect((-4.2, -1.0), (4.2, 0.1), stroke: black + 0.6pt, fill: luma(200))
content((0, -0.45), text(size: 9pt, weight: "bold")[Large Language Models (LLMs)])
}),
caption: [Hierarchische Einordnung: KI ⊃ ML ⊃ Deep Learning ⊃ LLMs.],
) <abb_hierarchie_ki>
#pagebreak()
*Neuronale Netze* lassen sich vereinfacht als parametrisierte Funktionsketten aus Schichten (Layern) beschreiben. Dabei geben die Schichten die Eingaben in zunehmend abstrakte Repräsentationen jeweils in die nächste Schicht weiter. Das Training erfolgt über eine Zielfunktion und Gradientenberechnung. In der Praxis geschieht dies meist über Backpropagation und Varianten des Gradientenabstiegs @goodfellow2016dl.
#figure(
cetz.canvas({
import cetz.draw: *
let r = 0.35
let lx = 3.0
let ny = 1.5
let ys-3 = (ny, 0, -ny)
let ys-2 = (ny / 2, -ny / 2)
let layers = (
(x: 0, ys: ys-3, labels: ($x_1$, $x_2$, $x_3$)),
(x: lx, ys: ys-3, labels: ($h_1$, $h_2$, $h_3$)),
(x: 2 * lx, ys: ys-2, labels: ($y_1$, $y_2$)),
)
// Kanten (zuerst zeichnen, damit Knoten darüber liegen)
for li in range(layers.len() - 1) {
let l1 = layers.at(li)
let l2 = layers.at(li + 1)
for y1 in l1.ys {
for y2 in l2.ys {
line(
(l1.x, y1), (l2.x, y2),
stroke: luma(160) + 0.4pt,
)
}
}
}
// Knoten
for layer in layers {
for (i, y) in layer.ys.enumerate() {
circle((layer.x, y), radius: r, stroke: black + 0.6pt, fill: white)
content((layer.x, y), layer.labels.at(i))
}
}
// Schichtbeschriftungen
let label-y = ny + 0.9
content((0, label-y), text(size: 9pt)[Eingabe])
content((lx, label-y), text(size: 9pt)[Verdeckte Schicht])
content((2 * lx, label-y), text(size: 9pt)[Ausgabe])
}),
caption: [Schematische Darstellung eines vollständig verbundenen Feedforward-Netzes.]
)
Ein einzelnes Neuron lässt sich als Transformation mit nachgeschalteter Aktivierungsfunktion formulieren:
$
z = sum_(i=1)^d w_i x_i + b, #h(1em) a = phi (z)
$
Typische Aktivierungsfunktionen ($a = phi (z)$) sind die Sigmoid-Funktion, der hyperbolische Tangens und ReLU #cite(<goodfellow2016dl>):
#grid(
columns: (1fr, 1fr, 1fr),
align: center,
column-gutter: 0.8em,
row-gutter: 0.4em,
text(weight: "bold")[Sigmoid],
text(weight: "bold")[tanh],
text(weight: "bold")[ReLU],
$ sigma(z) = frac(1, 1 + e^(-z)) $,
$ tanh(z) = frac(e^z - e^(-z), e^z + e^(-z)) $,
$ op("ReLU")(z) = max(0, z) $,
)
#figure(
cetz.canvas({
import cetz.draw: *
plot.plot(
size: (12, 7),
x-tick-step: 2,
y-tick-step: 1,
x-min: -6, x-max: 6,
y-min: -1.5, y-max: 6.5,
x-label: $x$,
y-label: $phi(x)$,
legend: "north-west",
{
plot.add(
style: (stroke: blue + 1.2pt),
domain: (-6, 6),
samples: 200,
label: [$sigma(x)$ (Sigmoid)],
x => 1 / (1 + calc.exp(-x)),
)
plot.add(
style: (stroke: orange + 1.2pt),
domain: (-6, 6),
samples: 200,
label: [$tanh(x)$],
x => {
let e2x = calc.exp(2 * x)
(e2x - 1) / (e2x + 1)
},
)
plot.add(
style: (stroke: green.darken(20%) + 1.2pt),
domain: (-6, 6),
samples: 200,
label: [ReLU $max(0, x)$],
x => calc.max(0, x),
)
},
)
}),
caption: [Beispielhafte Aktivierungsfunktionen (Sigmoid, tanh, ReLU).]
)
Die Optimierung erfolgt üblicherweise iterativ. Für Gradientenabstieg gilt in kompakter Form:
$
theta^(t+1) = theta^(t) - eta nabla_theta cal(L) (theta^(t))
$
LLMs unterscheiden sich von klassischen neuronalen Netzen nicht nur durch ihre Modellgröße, sondern vor allem durch ihre Zielsetzung: Sie sind Sprachmodelle -- Systeme, die Wahrscheinlichkeiten über Tokenfolgen schätzen und dadurch Texte fortsetzen, bewerten oder erzeugen können. Der Verarbeitungsablauf folgt dabei einem wiederkehrenden Muster.
Zunächst wird die Texteingabe in Token zerlegt. Token sind die kleinsten Verarbeitungseinheiten des Modells. je nach Tokenisierungsverfahren können dies Wörter, Wortteile oder einzelne Zeichen sein. Der Satz „Das System prüft den Status" wird beispielsweise in die Folge [„Das", „System", „prüft", „den", „Status"] überführt. Für Quellcode gilt dasselbe Prinzip: Identifier, Schlüsselwörter und Operatoren werden in Einheiten aufgeteilt @goodfellow2016dl.
Die resultierende Tokenfolge bildet den Kontext des Modells. Der Kontext umfasst alle Token, die das Modell in einem Verarbeitungsschritt gleichzeitig berücksichtigt. Aktuelle Modelle besitzen Kontextfenster von mehreren tausend bis über eine Million Token. Eingaben, die dieses Fenster überschreiten, müssen segmentiert oder komprimiert werden.
Auf Basis des Kontexts berechnet das Modell eine Wahrscheinlichkeitsverteilung über alle möglichen nächsten Token. Das gewählte Token wird an die bisherige Folge angefügt, und die erweiterte Folge dient als Eingabe für den nächsten Berechnungsschritt. Dieser Prozess wiederholt sich, bis ein Abbruchkriterium erreicht ist (z. B. ein Stopptoken oder eine maximale Ausgabelänge). Da das Modell jedes Token statistisch aus dem bisherigen Kontext ableitet, können Ausgaben sprachlich konsistent wirken, ohne dass fachliche Korrektheit gewährleistet ist. Formal lässt sich das Trainingsziel als Maximierung der bedingten Log-Likelihood beschreiben:
$
max_(theta) sum_(t=1)^T log p_(theta) (x_t mid x_(<t))
$
Hierbei bezeichnet $x_(<t)$ die bisherige Tokenfolge und $p_(theta)(x_t mid x_(<t))$ die vom Modell geschätzte Wahrscheinlichkeit für das nächste Token $x_t$.
In der aktuellen Modellgeneration dominiert die Transformer-Architektur @vaswani2017attention, deren Kernmechanismus die Self-Attention ist. Self-Attention ermöglicht es dem Modell, bei der Verarbeitung jedes Tokens die Beziehungen zu allen anderen Token im Kontext zu gewichten. Formal wird dies als gewichtete Kombination von Value-Vektoren beschrieben, wobei die Gewichte aus Query-Key-Ähnlichkeiten berechnet werden #cite(<vaswani2017attention>):
$
op("Attention") (Q, K, V) = op("softmax") ((Q K^T) / sqrt(d_k)) V
$
/*
#figure(
image("../../Abbildungen/abb_2_3_attention_heatmap.png", width: 85%),
caption: [Schematisches Beispiel einer Attention-Gewichtsmatrix (illustrativ).]
)
*/
LLMs werden daher im Software Engineering eingesetzt, weil sie sowohl Artefakte in Prosa (z. B. Anforderungen, Kommentare) als auch Codeartefakte (z. B. Klassen, Funktionen, DB Schemata) verarbeiten können. Verscheidene Arbeiten versuchen daher LLM-Anwendungen nach Aufgabenklassen wie Codegenerierung, Codezusammenfassung, Fehlersuche, Testgenerierung oder Dokumentation zu ordnen @fan2023llmse @salem2024surveyllmse @llm4se2024slr @llm4se2024survey
_Aus den folgenden Eigenschaften der LLMs ergeben sich daher für diese Arbeit Konsequenzen:_
_- *Kontextfenster:* Modelle verarbeiten Eingaben nur bis zu einer maximalen Tokenanzahl. Längere Artefakte müssen segmentiert oder komprimiert werden._\
_- *Tokenisierung:* Quellcode und Fachsprache werden in Token zerlegt. Dies beeinflusst, wie gut Identifier, Struktur und Domänenterminologie repräsentiert werden._\
_- *Generativer Charakter:* Ausgaben sind nicht deterministisch. Temperatur, Sampling-Strategien und Promptform beeinflussen Reproduzierbarkeit._
#heading(level: 3)[LLM Training und Prompting]
LLMs werden typischerweise in mehreren Phasen entwickelt. In einer Vortrainingsphase lernen Modelle aus großen Text- und Codekorpora statistische Regularitäten. Für den Einsatz als Assistenzsysteme werden Modelle häufig zusätzlich auf Anweisungen und Dialogformate ausgerichtet („instruction tuning"). Der GPT-4 Technical Report beschreibt diese Ausrichtung auf Systemebene und diskutiert Safety- und Evaluationsaspekte, ohne die vollständige Trainingspipeline offen zu legen @openai2023gpt4.
Im Engineering-Kontext ist der Prompt damit nicht nur Eingabe, sondern auch ein Steuerungsinstrument.\ Für diese Arbeit sind vor allem folgende Hebel relevant:
/ Aufgabe: Ziel, gewünschtes Artefaktformat, Definition von Begriffen und Abgrenzung (z. B. „Requirement" vs. „Designentscheidung").
/ Kontextwahl: Welche Code- und Textartefakte werden bereitgestellt, und welche Teile werden bewusst ausgeblendet, um Überinterpretation zu begrenzen?
/ KI Leitplanken: Belegpflicht, Kennzeichnung unsicherer Aussagen, feste Templates, DOs and DONTs.
Da LLMs ein begrenztes Kontextfenster besitzen, wird in Forschung und Praxis häufig Retrieval-Augmented Generation (RAG) eingesetzt: Relevante Textstellen werden zunächst über Suche/Retrieval ausgewählt und anschließend als Kontext in die Generierung eingebracht. #cite(<lewis2020rag>, form: "prose") beschreiben dieses Grundprinzip für wissensintensive Aufgaben. Für Requirements-Extraktion aus Legacy-Code ist RAG naheliegend, weil relevante Regeln, Konfigurationen und UI-Strings über große Repositories verteilt sind und eine „Alles in den Prompt"-Strategie nicht skaliert.
Prompting-Strategien wie Chain-of-Thought können die Qualität komplexer Ableitungen verbessern, bergen im Requirements-Kontext jedoch ein Risiko: Längere Begründungen können plausibel wirken und dadurch Fehlannahmen stabilisieren. #cite(<wei2022cot>, form: "prose") zeigen Chain-of-Thought als wirksame Prompttechnik.\ \ _Für diese Arbeit folgt daraus vor allem, dass Begründungen stets mit Artefaktbelegen gekoppelt werden müssen und nicht als eigenständige Evidenz gelten._
/*
#heading(level: 3)[LLMs für Code: Spezialisierung, Stärken und Grenzen]
Neben allgemeinen Modellen existieren code-spezialisierte LLMs, die auf Codekorpora vortrainiert oder nachtrainiert wurden. Ein prominentes Beispiel ist Code Llama, dessen Technical Report Training und Evaluationsaufbau beschreibt und die Ausrichtung auf Codeaufgaben explizit macht @roziere2023codellama. Aus praktischer Sicht sind bei code-spezifischen Modellen typischerweise drei Stärken zu beobachten:
- **Syntaxnahe Mustererkennung:** Wiederkehrende Idiome, Framework-Patterns und typische Kontrollstrukturen werden zuverlässig erkannt.
- **Semantische Zusammenfassung:** Funktionen und Module lassen sich in natürliche Sprache übertragen, inklusive grober Zweckbeschreibung.
- **Transformation und Vorschläge:** Refactorings, Testideen oder API-Skizzen können generiert und iterativ verfeinert werden.
Den Stärken stehen systematische Grenzen gegenüber. LLMs „verstehen" Code nicht im Sinne einer formalen Semantik. Sie approximieren Bedeutung über Muster aus Trainingsdaten und aus dem gegebenen Kontext. Insbesondere in Legacy-Systemen mit proprietären Frameworks, kundenspezifischen Erweiterungen und historisch gewachsenen Konventionen ist die Wahrscheinlichkeit hoch, dass Modelle plausible, aber falsche Erklärungen liefern. Genau diese Plausibilität ist im Requirements-Kontext kritisch, weil Text als Spezifikation eine höhere Autorität erhält als eine rein technische Zusammenfassung.
*/
#heading(level: 3)[Qualitätsrisko Halluzinationen]
Halluzinationen bezeichnen Ausgaben, die syntaktisch korrekt und plausibel wirken, aber nicht durch Eingabedaten oder Weltwissen gedeckt sind. #cite(<ji2023hallucination>, form: "prose") liefern die Begrifflichkeit und diskutieren Detektions- und Lösungsansätze. Für Requirements ist die Gefahr besonders kritisch, weil falsche Requirements nicht als „Bug" auffallen, sondern als scheinbar saubere Spezifikation in Architektur- und Funktionsentscheidungen einfließen können.
Zusätzlich zu Halluzinationen sind zwei weitere Verlässlichkeitsthemen relevant:
/ Daten- und Domänenbias: Modelle spiegeln Verteilungen und Annahmen aus Trainingsdaten wider @bender2021stochastic. Taucht eine falsche Aussage in Trainingsdaten häufig auf, wird sie vom Modell übernommen und als Wahrheit ausgegeben.
/ Reproduzierbarkeit: Kleine Promptänderungen oder Parameterunterschiede können zu unterschiedlichen Ergebnissen führen. Für einen engineeringfähigen Prozess sind daher Leitplanken (z. B. feste Templates, deterministische Einstellungen, versionierte Prompts) notwendig.
_Für diese Arbeit folgt daraus, dass LLM-Ausgaben im Requirements-Kontext nicht als Wahrheit", sondern als Vorschlag zu behandeln sind. Erst durch Traceability (Belege) und Validierung (Expertenreview, Laufzeitchecks) wird aus einer Hypothese eine belastbare Anforderung._
#heading(level: 3)[LLMs im Requirements Engineering: Stand der Forschung]
Aktuelle Arbeiten untersuchen, wie LLMs Texte erzeugen, umformulieren und strukturieren können, und verschieben den Schwerpunkt damit gegenüber klassischen NLP/IR-Ansätzen mit begrenzten Ausgaben (Labels, Links, Hinweise) von reinen Analysen der Zusammenhänge hin zu tatsächlicher Fomrulierung von Requirements. Dieser Übergang ist aber nicht ohne Risiken: Einerseits entsteht ein großes Potential, andererseits steigt das Risiko, dass sprachlich "gute" Texte als Spezifikation akzeptiert werden, obwohl die fachliche Basis unzureichend oder fehlerhaft ist (siehe Haluzinationen) @ji2023hallucination @hemmat2025directions.
Eine systematische Übersicht ordnet die LLM-Nutzung im RE dabei entlang klassischer Prozessschritte ein und nennt als wiederkehrende Problemfelder _Qualitätssicherung, Nachvollziehbarkeit und Domänenabhängigkeit_ @hemmat2025directions. Ein weiteres Review zum LLM-Einsatz im Requirements Engineering diskutiert den Einsatz entlang typischer RE-Aktivitäten (Analyse, Spezifikation, Validierung) und hebt als zentrale Herausforderungen inkonsistente Ergebnisse durch limitierte Kontextgrößen und begrenztes Domänenwissen hervor @marques2024chatgptre.
Dabei lassen sich aktuelle LLM-Arbeiten grob folgenden Themen zusammenfassen:
/ Strukturierung und (Re-)Formulierung von Requirements: Untersucht wird, wie LLMs natürlichsprachliche Anforderungen in strukturiertere Formen überführen können @norheim2024structuring, sowie die automatische Umstrukturierung von Software Requirements Specifications mit dem Ziel, Standardkonformität zu erhöhen @okamoto2025restructuring.
/ Qualitätsunterstützung und Analyse: ChatGPT wurde für die Inkonsistenzdetektion in naturalsprachlichen Requirements evaluiert @fantechi2023inconsistency; weitere Arbeiten untersuchen LLM-gestützte Assistenz zur Verbesserung der Requirements-Vollständigkeit @luitel2024completeness.
/ Anforderungserhebung (Elicitation) und Perspektivenwechsel: LLMs können zur Generierung wertorientierter User Stories als "Inspirationsimpulse" eingesetzt werden @marczak2023humanvalue. Diese Richtung ist für Reverse Requirements Engineering insofern relevant, weil sie zeigt, wie LLMs fehlende Stakeholder-Sichten ergänzen können, ohne den Code als Primärbeleg zu ersetzen.
/ Domänenspezifische Requirements (Safety/Compliance): Betrachtet wurden LLMs bei der Engineering-Unterstützung von Safety Requirements im Kontext autonomen Fahrens @nouri2024safety sowie für rechtliche Compliance- und Regulationsanalyse @hassani2024legal. Solche Arbeiten verdeutlichen, dass LLMs nicht nur Text umformulieren, sondern auch regulatorische Anforderugen (Normen, Regeln) einbinden können.
Insgesamt ist die Studienlage bisher uneinheitlich. Viele Arbeiten sind kurze Workshopbeiträge oder erste Vorstudien mit kleinen Datensätzen, die automatische Messungen mit Experteneinschätzungen mischen. Auch die verwendeten Prompts, Modellversionen und Einstellungen sind selten einheitlich dokumentiert, wodurch sich Ergebnisse schwer wiederholen lassen @fan2023llmse @hemmat2025directions.
Für Reverse Requirements Engineering lässt sich der Nutzen damit präzisieren: LLMs können Kandidaten-Requirements aus großen Artefaktmengen (Code, Kommentare, UI-Strings, Konfiguration) herauslesen und verdichten und in eine konsistente Spezifikationsform überführen. Die fachliche Belastbarkeit entsteht jedoch erst durch Traceability zu Codebelegen und die Validierung durch Experten, insbesondere bei Safety-, Compliance- und Abrechnungslogik.
#heading(level: 3)[Absicherung: Human-in-the-loop, Belege und Prozesskontrollen]
Die Literatur legt nahe, dass LLMs im Software Engineering dann robust eingesetzt werden können, wenn sie in einen Prozess eingebettet sind, der Fehler systematisch begrenzt @fan2023llmse @hemmat2025directions. Für die Requirements-Extraktion aus Legacy-Code sind folgende Kontrollen praxisnah:
/ Belegpflicht (Evidence-First): Jedes generierte Requirement erhält mindestens einen konkreten Beleg (Datei/Komponente/Query/GUI-String) sowie eine kurze Begründung, warum der Beleg die Aussage trägt.
/ Trennung von Fakt und Interpretation: Technische Fakten (z. B. „Status = 'Closed' verhindert Update") werden getrennt von fachlicher Interpretation (z. B. „Abgeschlossene Aufträge sind schreibgeschützt") dokumentiert.
/ Mehrstufige Validierung: Automatische Checks (z. B. Linting auf Verbformen, Konsistenzregeln) werden mit Expertenreview kombiniert.
/ Reproduzierbarkeit: Versionierung von Promptvorlagen, Modellversionen und Kontextzuschnitten, um Ergebnisse vergleichbar zu machen.
Diese Kontrollen adressieren nicht alle Risiken, reduzieren aber die typischen Fehlerklassen (Halluzination, Überinterpretation, fehlende Konsistenz) und schaffen die Grundlage für eine belastbare Evaluation.
#heading(level: 3)[Qualitätsbewertung und Messgrößen im Requirements-Kontext] <sec_re_qualitaet>
Die Qualität von LLM-Ergebnissen wird in vielen Arbeiten mit allgemeinen Textmetriken oder aufgabenspezifischen Benchmarks bewertet. Für die Requirements-Extraktion aus Code reichen solche Metriken nicht aus, da es hier weniger um sprachliche Ähnlichkeit geht, sondern um fachliche Korrektheit, Prüfbarkeit und Nachvollziehbarkeit @hemmat2025directions @marques2024chatgptre. Eine sinnvolle Bewertung orientiert sich daher an RE-Kriterien und unterscheidet drei Dimensionen:
/ Statement-Qualität: Ist ein Requirement eindeutig, vollständig im Satzbau, frei von nicht belegten Annahmen und mit Akzeptanzkriterium bzw. Prüfidee versehen?
/ Set-Qualität (Vollständigkeit): Deckt die Menge der Requirements alle relevanten Prozesse und Varianten vollständig ab, ist sie in sich konsistent und frei von Doubletten? Die Vollständigkeit ist dabei die zentrale Eigenschaft, weil ein fehlendes Requirement im Migrationskontext zu Funktionsverlust führen kann, während Inkonsistenzen oder Doubletten in einem Review nachträglich aufgelöst werden können.
/ Traceability-Qualität: Sind Belege reproduzierbar auffindbar (z. B. Dateipfad, Methode, SQL-Query), und lässt sich die Ableitung von „Beleg → Requirement" nachvollziehen?
Für Legacy-Migrationen ist zudem die Fehlerkostenperspektive entscheidend. Ein fehlendes Requirement kann zu Funktionsverlust führen, ein falsches Requirement kann zu fehlerhaften Designentscheidungen führen, und ein unpräzises Requirement verursacht Review- und Nacharbeit. Daraus folgt eine pragmatische Bewertung: Requirements mit hoher Migrationskritikalität (z. B. Sicherheitsregeln, Abrechnungslogik, Berechtigungen) sollten strengere Evidenzanforderungen und intensivere Reviews erhalten als periphere Funktionen. Dieses Prinzip ist kompatibel mit der risikobasierten Priorisierung von Qualitätsanforderungen @glinz2008quality und lässt sich auf Funktionsanforderungen übertragen.
/*
#heading(level: 3)[Zwischenfazit zu 2.2]
LLMs liefern im Software Engineering eine leistungsfähige Assistenz für Analyse, Zusammenfassung und Textproduktion, sind jedoch nicht verlässlich im Sinne formaler Korrektheit @fan2023llmse @ji2023hallucination. Für Requirements ist der entscheidende Punkt, dass die Qualität nicht an der sprachlichen Glätte, sondern an Nachvollziehbarkeit und Prüfbarkeit hängt. Daraus folgt für diese Arbeit ein designierter "Sicherheitsgurt": Evidence-First, Traceability und Human-in-the-loop sind keine Zusatzoptionen, sondern Kernelemente des Vorgehens.
*/
@@ -0,0 +1,59 @@
#heading(level: 2)[Legacy-Modernisierung und Stand der Forschung]
#heading(level: 3)[Charakteristika von Legacy-Systemen]
Legacy-Systeme sind nicht allein durch ihr Alter, sondern durch ihren Kontext definiert. Sie tragen geschäftskritische Funktionen, sind über lange Zeit erweitert worden und weisen oft starke technische und organisatorische Abhängigkeiten auf. Typische Merkmale sind enge Kopplung, heterogene und zersplitterte Technologien, schwer austauschbare oder sogar schon abgekündigte Komponenten und unzureichende Dokumentation @bisbal1999legacy. Gerade die fehlende Dokumentation ist für Modernisierungsvorhaben problematisch, weil Entscheidungen ohne belastbare Anforderungsbasis zu Funktionsverlusten und Fehlentscheidungen führen können.
Im ERP-Kontext verschärfen sich diese Merkmale häufig durch eine ausgeprägte Domänenkomplexität, da Geschäftsregeln variantenreich und teilweise kundenspezifisch sind. Hinzu kommt eine starke Datenzentrierung. Prozesse hängen stark von Datenmodellen, Stammdatenqualität und historisch gewachsenen Datenkonventionen ab. Verstärkt wird dies durch eine hohe Integrationsvielfalt, da Schnittstellen zu Drittsystemen (z. B. Buchhaltung, Shop, Dokumentenmanagement) über Jahre organisch entstanden sind.
Damit wird nachvollziehbar, warum Requirements-Extraktion aus der Codebasis nicht nur ein Dokumentationsprojekt ist. Es dient zeitglich auch zur Risikoreduktion.
#heading(level: 3)[Modernisierungsstrategien]
Eine Modernisierung kann durch unterschiedliche Methoden umgesetz werden, von "Lift-and-Shift" bis zur vollständigen Neuimplementierung "auf der grünen Wiese". Die Literatur betont wiederholt, dass die Wahl einer Strategie von Risiko, Zielarchitektur und verfügbaren Ressourcen abhängt und daher explizit geplant werden sollte @sneed1995planning. Eine zentrale Aussage ist dabei, dass Reengineering nicht als rein technischer Umbau verstanden werden darf. Ohne fachliche Leitplanken entstehen technische Verbesserungen, die am Bedarf vorbeilaufen oder bestehende Fachlogik unabsichtlich verändern. Zudem können gewonnene fachliche Erkenntnisse ohne klare Dokumentation und Nachvollziehbarkeit nicht in die Zielarchitektur überführt werden, was zu einem erneuten Anforderungsdefizit führt.
_Aus Sicht dieser Arbeit lassen sich Modernisierungsmethodiken pragmatisch entlang zweier Dimensionen einordnen: (1) Wie stark wird die bestehende Implementierung weitergenutzt? (2) Wie stark wird die Zielarchitektur verändert? Daraus ergeben sich typische Strategietypen, die in der Praxis auch kombiniert auftreten:_
+ *Weiterbetrieb mit Hülle (Wrapping):* Die Legacy-Logik bleibt bestehen, wird aber über neue Schnittstellen oder eine neue GUI zugänglich gemacht. Vorteil ist geringe Eingriffstiefe; Nachteil ist, dass technische Schulden und Engpässe erhalten bleiben.
+ *Schrittweise Modularisierung:* Teile der Legacy-Anwendung werden sukzessive in neue Komponenten überführt, während andere Teile weiterlaufen. Vorteil ist Risikostreuung und frühe Nutzenrealisierung; Nachteil ist erhöhte Integrationskomplexität während der Übergangsphase.
+ *Reengineering/Refactoring:* Die bestehende Logik wird strukturell überarbeitet (z. B. Entkopplung, Schichten, bessere Testbarkeit), ohne den Funktionsumfang grundsätzlich zu verändern. Vorteil ist bessere Wartbarkeit; Nachteil ist hoher Analyseaufwand, gerade ohne Requirementsbasis.
+ *Neuimplementierung mit Funktionsparität:* Die Legacy-Logik wird auf neuer Technologie nachgebaut, häufig mit dem Anspruch, zunächst funktional äquivalent zu sein. Vorteil ist saubere Zielarchitektur; Nachteil ist die hohe Abhängigkeit von vollständigen, korrekten Requirements.
Für ein ERP-System ist die Wahl einer Strategie stark datengetrieben. Datenmodelle und Businesslogik definieren die Leitplanken einer Migration. Damit steigt die Wichtigkeit von Requirements, die Datenobjekte, Zustandsmodelle und Integrationspunkte ausdrücken. Besonders kritisch sind dabei Anforderungen, die in der Legacy-Implementierung als implizite Konvention existieren (z. B. Statuscodes, historische Sonderfälle, kundenspezifische Maskenlogik), weil sie ohne gezielte Extraktion und Validierung leicht verloren gehen.
#heading(level: 3)[Zielarchitekturen: Web, Cloud und „Cloud-native"]
Die Modernisierung vieler Legacy-Anwendungen zielt auf webbasierte, plattformunabhängige Oberflächen und auf Betriebsmodelle, die Skalierung, automatisiertes Deployment und schnelle Iteration unterstützen. Eine systematische Mapping Study fasst zusammen, welche Merkmale cloud-nativer Anwendungen in Forschung und Praxis wiederkehren @kratzke2017cloudnative. Dazu zählen typischerweise automatisierte Bereitstellung, resiliente Komponenten, horizontale Skalierung und eine stärkere Trennung von Build- und Run-Umgebungen.
Im selben Zusammenhang werden Microservices häufig als Architekturstil diskutiert. Eine Analyse der Forschung zu Microservices zeigt wiederkehrende Problemfelder, unter anderem die Wahl der richtigen Servicegranularität, die erhöhte Komplexität im Betrieb und Anforderungen an Observability @pahl2016microservices. Für Migrationsprojekte ist daraus eine pragmatische Schlussfolgerung ableitbar: Modularisierung ist ein Ziel, erzeugt aber zugleich neue Anforderungen (z. B. Deployment-Pipelines, Monitoring, Sicherheitskonzepte), die im Requirements-Set sichtbar sein müssen.
Für die Requirementsentwicklung bedeutet diese neue Zielarchitektur eine Verschiebung des Schwerpunktes. Während in klassischen Server/Client-Architekturen die fachliche Funktionslogik oft dominiert, rücken in Web- und Cloud-Kontexten auf den Betrieb bezogene (Deployment, Monitoring, Skalierung) und sicherheitsrelevante Qualitätsmerkmale (SaaS, Multi-Tennanting) stärker in den Vordergrund. ISO/IEC 25010:2011 bietet hierfür eine hilfreiche Taxonomie @iso25010_2011. Für Modernisierungsvorhaben lassen sich vor allem folgende Qualitätsmerkmale als wiederkehrend beobachten:
/ Sicherheit: Identitäten, Rollenmodelle, Mandantenfähigkeit, Auditierbarkeit.
/ Zuverlässigkeit: Fehlerresistenz, Wiederanlauf, Degradationsverhalten.
/ Performance-Effizienz: Antwortzeiten, Lastverhalten, Skalierungsgrenzen.
/ Wartbarkeit: Änderbarkeit, Testbarkeit, Modularität und technische Schuld.
/ Kompatibilität und Interoperabilität: Schnittstellenstabilität, Integrationsfähigkeit mit Drittsystemen.
Diese Merkmale sind nicht neu, ihre Sichtbarkeit im Projekt nimmt jedoch zu, weil Cloud- und Webbetrieb ein engeres Zusammenspiel von Entwicklung und Betrieb erzwingt.
_Für das Reverse Requirements Engineering im Rahmen dieser Arbeit folgt daraus, dass der Blick auf die Legacy-Codebasis systematisch um Betriebs- und Sicherheitsanforderungen ergänzt werden muss, auch wenn diese im Code nur indirekt sichtbar sind (z. B. über Deployment-Skripte, Konfigurationen, Logging-Policies oder Rechteprüfungen)._
#heading(level: 3)[Stand der Forschung: KI-Unterstützung in Modernisierungsvorhaben]
Die Forschung zu LLM-Unterstützung bei Moderniesierungen steh im Vergleich zu klassischen Reengineering-Ansätzen noch an den Anfängen. Die Übersichten zu LLM4SE @fan2023llmse @llm4se2024slr zeigen, dass ein Teil der Arbeiten auf Codeverständnis, Dokumentation und Artefakttransformation zielt. Spezifisch für Requirements Engineering zeigen Reviews und SLRs erste konkrete Forschungsrichtungen @marques2024chatgptre @hemmat2025directions.
Aus dieser Literatur lassen sich zwei Aussagen ableiten. Zum einen sind LLMs besonders stark in der Strukturierung und sprachlichen Formulierung. Also dort, wo aus heterogenen Artefakten ein konsistenter Text entstehen muss. Zum anderen benötigen LLMs technische und organisatorische Sicherungen, wenn Ergebnisse als Entscheidungsgrundlage in Migrationen dienen sollen (z. B. Belege, Review, reproduzierbarer Prozess).
_Dies ist auch die Zentrale Motivation dieser Arbeit: Eine Legacy-Modernisierung benötigt belastbare Requirements, die im Legacy-Kontext oft fehlen. LLMs sind als Assistenz zur Rekonstruktion naheliegend, müssen jedoch methodisch so eingesetzt werden, dass Verlässlichkeit und Nachvollziehbarkeit systematisch erhöht werden._
/*
#heading(level: 3)[Zwischenfazit zu 2.3]
Legacy-Modernisierung ist ein sozio-technisches Vorhaben, das technische Umbauten und fachliche Zielsetzungen integrieren muss @bisbal1999legacy @sneed1995planning. Moderne Zielarchitekturen (Web/Cloud) verschieben zudem die Anforderungslandschaft, weil Betriebs- und Sicherheitsanforderungen stärker in den Vordergrund treten @kratzke2017cloudnative @security2014legacycloud. Für die vorliegende Arbeit folgt daraus, dass Requirements-Extraktion nicht nur der Funktionsrekonstruktion dient, sondern die Grundlage für Migrationsentscheidungen, Priorisierung und Qualitätssicherung bildet.
*/
#heading(level: 3)[Kapitelzusammenfassung und Anschluss]
Die drei Themenblöcke dieses Kapitels greifen ineinander. Requirements Engineering liefert Kriterien, um Anforderungen prüfbar und nachvollziehbar zu formulieren @iso29148_2018. Reverse Requirements Engineering überträgt diese Kriterien in einen Kontext, in dem Anforderungen aus bestehenden Artefakten rekonstruiert werden müssen. Large Language Models können bei dieser Rekonstruktion unterstützen, sind aber fehleranfällig und benötigen Prozesskontrollen, vor allem gegen Halluzinationen und Überinterpretation. Legacy-Modernisierung schließlich liefert die praktische Motivation und zeigt, warum eine belastbare Anforderungsbasis migrationskritisch ist @bisbal1999legacy @sneed1995planning.
+40 -56
View File
@@ -4,98 +4,82 @@
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Fallstudie c-entron GmbH (ca. 6 Seiten)]
#heading(level: 1)[Fallstudie c-entron GmbH]
Dieses Kapitel beschreibt den Anwendungskontext der Arbeit in Form einer Fallstudie. Im Mittelpunkt steht eine gewachsene, Windows-basierte ERP-Software der c-entron GmbH, die im Rahmen einer Modernisierung auf eine webbasierte Plattform überführt werden soll. Ziel des Kapitels ist es, die fachlichen und technischen Rahmenbedingungen so zu strukturieren, dass die Anforderungen an ein KI-gestütztes Reverse Requirements Engineering nachvollziehbar werden. Dazu werden zunächst Unternehmenskontext und Legacy-Software eingeordnet und anschließend die Migrationsstrategie sowie die spezifischen Herausforderungen zusammengefasst.
Dieses Kapitel beschreibt die Anwendung auf die sich diese Arbeit bezieht. Betrachtet wird die Windows-basierte ERP-Software der c-entron GmbH, die auf eine webbasierte Plattform überführt werden soll. Ziel ist es, die fachlichen und technischen Rahmenbedingungen so darzustellen, dass die Anforderungen an ein KI-gestütztes Reverse Requirements Engineering nachvollziehbar werden. Zunächst werden Unternehmenskontext und Legacy-Software eingeordnet, anschließend folgen Migrationsstrategie und spezifische Herausforderungen.
#heading(level: 2)[Unternehmenskontext und Legacy-Software]
#heading(level: 3)[Unternehmens- und Domänenkontext]
Die c-entron GmbH (Ulm) wird in dieser Arbeit als Fallunternehmen betrachtet. Das Unternehmen entwickelt und betreibt eine ERP-Suite, die sich an IT-Systemhäuser und deren typische Abläufe richtet. Im Vergleich zu neu entstehenden SaaS-Produkten ist die betrachtete Lösung über einen langen Zeitraum in einem stabilen Marktsegment gereift. Damit ist der Funktionsumfang breit, zugleich ist die Implementierung historisch gewachsen und enthält produktionsnahe Randfälle, Ausnahmen und kundenbezogene Varianten.
Die c-entron GmbH (Ulm) entwickelt und betreibt eine ERP-Suite für IT-Systemhäuser. Die Software ist über mehrere Jahrzehnte gewachsen, der Funktionsumfang entsprechend breit. Mit der historischen Tiefe gehen produktionsnahe Sonderfälle und kundenbezogene Varianten einher, die im Tagesgeschäft funktionieren, aber nur teilweise dokumentiert sind.
Für den Kontext dieser Arbeit sind drei Aspekte wesentlich:
Für diese Arbeit ist die Software relevant, weil sie Anforderungen in einer Form enthält, die für Reverse Requirements Engineering typisch schwierig ist. Geschäftskritische Prozesse wie Auftragsabwicklung, Lager und Fakturierung sind nicht nur in einzelnen Masken abgebildet, sondern in Validierungen, Statusübergängen und Datenmodellrestriktionen verteilt. Hinzu kommt die Kopplung an weitere Anwendungen über Import- und Exportschnittstellen. Aus diesen Artefakten müssen für die Modernisierung stabile Geschäftsregeln und Datenbeziehungen abgeleitet werden, die als Grundlage einer webbasierten Neuimplementierung dienen.
- **Geschäftskritische Domäne:** ERP-Systeme bilden Kernprozesse ab. Änderungen wirken direkt auf Abrechnung, Lieferfähigkeit und Projektsteuerung.
- **Hohe Integrationsdichte:** ERP-Funktionen sind typischerweise über Schnittstellen, Datenimporte/-exporte und angebundene Drittsysteme mit weiteren Anwendungen gekoppelt.
- **Regel- und Datenorientierung:** Ein großer Teil der Logik manifestiert sich in Validierungen, Statusübergängen, Berechtigungsprüfungen und Datenmodellrestriktionen.
Die Software deckt nach vorliegendem Projektkontext unter anderem Auftragsabwicklung, Lagerfunktionen, Fakturierung sowie Projektabrechnung ab. Für die Modernisierung ist daher nicht nur die Rekonstruktion einzelner Masken oder Funktionen relevant, sondern vor allem die Ableitung stabiler Geschäftsregeln und Datenbeziehungen, die als Basis für eine webbasierte Neuimplementierung dienen.
#heading(level: 3)[Technologischer Ist-Stand]
#heading(level: 3)[Technologischer Ist-Stand (Legacy-Charakteristik)]
Die ERP-Suite ist als Windows-Anwendung in klassischer Client/Server-Architektur über Jahrzehnte gewachsen. Für die Modernisierung sind nicht das Alter, sondern die Kopplung der Komponenten und die lückenhafte Dokumentation entscheidend. Folgende Merkmale sind für die Fallstudie besonders relevant:
Die betrachtete ERP-Suite ist als Windows-basierte Anwendung in einer klassischen Client/Server-Architektur gewachsen. Aus Sicht der Modernisierung ist weniger das „Alter“ entscheidend als die Kombination aus technischer Kopplung, historischer Evolution und begrenzter Dokumentation. Diese Merkmalskombination ist typisch für Legacy-Systeme @bisbal1999legacy.
/ Enge Kopplung zwischen UI, Fachlogik und Persistenz: Geschäftsregeln sind nicht in einer Schicht isoliert, sondern verteilen sich über mehrere Komponenten. So liegen Validierungen im Frontend-Code, während weitere Regeln als Trigger in der MSSQL-Datenbank realisiert sind.
/ Implizite Regeln in Code und Daten: Ein Teil der Anforderungen ist nicht explizit dokumentiert, sondern ergibt sich aus Validierungen, Standardwerten und Datenmodellannahmen.
/ Evolution über lange Zeiträume: Funktionserweiterungen und Fehlerkorrekturen haben zu Sonderfällen und Workarounds geführt, die fachlich begründet, aber selten als Requirement festgehalten sind. Regeln zum gleichen Fachthema (z. B. Preisberechnung) werden auf unterschiedlichen Masken nicht einheitlich angewendet, was die genaue Definition der Regel erschwert.
/ Heterogene Artefakte: Neben Quellcode existieren Konfigurationen, UI-Texte, Reportdefinitionen sowie Import- und Exportlogiken, die Anforderungen indirekt spiegeln.
Für die Fallstudie lassen sich die folgenden, für Requirements-Rückgewinnung relevanten Charakteristika bündeln:
- **Enge Kopplung zwischen UI, Fachlogik und Persistenz:** Geschäftsregeln sind nicht konsistent in einer Schicht isoliert, sondern verteilen sich über unterschiedliche Komponenten.
- **Implizite Regeln in Code und Daten:** Ein Teil der Anforderungen ist nicht explizit dokumentiert, sondern ergibt sich aus Validierungen, Prüfpfaden, Standardwerten und Datenmodellannahmen.
- **Evolution über lange Zeiträume:** Funktionserweiterungen und Fehlerkorrekturen führen zu Sonderfällen und Workarounds, die fachlich begründet sein können, aber selten als Requirements festgehalten sind.
- **Heterogene Artefakte:** Neben Quellcode existieren Konfigurationen, UI-Texte, Reportdefinitionen, Skripte oder Import-/Exportlogiken, die Anforderungen indirekt spiegeln.
In der Summe ist der Ist-Zustand damit ein realitätsnaher Prüfstand für Reverse Requirements Engineering: Die Anforderungen liegen nicht als konsolidierte Spezifikation vor, sondern müssen aus einem Bündel technischer Artefakte rekonstruiert und anschließend fachlich validiert werden.
Die Software ist damit ein geeigneter Gegenstand für Reverse Requirements Engineering. Die Anforderungen liegen nicht als gesammelte Dokumentation vor, sondern müssen erst rekonstruiert werden.
#heading(level: 3)[Dokumentations- und Wissenslage]
Für das betrachtete Modernisierungsvorhaben ist die Ausgangslage geprägt durch fehlende oder nur teilweise gepflegte Anforderungsdokumentation. In Standards des Requirements Engineering wird eine nachvollziehbare Spezifikation mit Qualitätskriterien wie Eindeutigkeit, Verifizierbarkeit und Traceability als Zielbild beschrieben @iso29148_2018 @ieee830_1998. Im Fallunternehmen sind solche Artefakte nicht in ausreichender Tiefe verfügbar, sodass wesentliche Informationen in folgenden Quellen gebunden sind:
Für das Modernisierungsvorhaben liegt keine schriftliche Anforderungsdokumentation vor. Vollständigkeit, Verifizierbarkeit und Traceability als zentrale Qualitätskriterien des Requirements Engineering müssen daher aus anderen Quellen hergestellt werden. Fachliche Regeln sind primär in der *Implementierung und im Laufzeitverhalten* gebunden. Änderungsanlässe, Bugfixes und Kundenanpassungen lassen sich ergänzend aus der *Change-Historie* in Issue-Tracker, Commits und Releases rekonstruieren. Ein wesentlicher Teil liegt zudem als *Erfahrungswissen einzelner Mitarbeiter* vor; dieses Domänenwissen ist personenbezogen und stellt ein Risiko bei Personalwechsel dar.
- **Implementierung und Laufzeitverhalten:** Fachliche Regeln werden praktisch durch Codepfade und Datenzustände realisiert.
- **Change-Historie und Tickets:** Änderungsanlässe, Bugfixes und Kundenanpassungen sind häufig über Issue-Tracker, Commits oder Releases rekonstruierbar.
- **Erfahrungswissen einzelner Mitarbeiter:** Domänenwissen ist personenbezogen und damit ein Risiko bei Personalwechsel.
Für die vorliegende Arbeit folgt daraus, dass der Wert eines KI-gestützten Reverse Requirements Engineering nicht in der Generierung „gut klingender“ Spezifikationstexte liegt, sondern in der systematischen Extraktion belegbarer Aussagen, die als Requirements prüfbar sind und die spätere Migration absichern.
_Für diese Arbeit folgt daraus, dass der Wert eines KI-gestützten Reverse Requirements Engineering nicht in der Generierung „gut klingender" Spezifikationstexte liegt, sondern in der systematischen Extraktion belegbarer Aussagen mit Referenz zu existierenden Artefakten, die als Requirement prüfbar sind und die spätere Migration absichern._
#heading(level: 3)[Relevante Artefakte der Fallstudie]
Für die Anforderungen an das Vorgehen in späteren Kapiteln ist es hilfreich, den Artefaktraum der Fallstudie zu strukturieren. Im Kontext der ERP-Modernisierung sind insbesondere folgende Artefaktklassen als Requirements-Träger relevant:
Für das Vorgehen in den folgenden Kapiteln ist es hilfreich, die vorhandenen Artefakte zu ordnen. In der ERP-Modernisierung sind insbesondere folgende Artefaktklassen Träger von Requirements:
1. **Quellcode:** Implementierte Regeln, Berechtigungen, Statuslogik, Datenzugriffe.
2. **Konfiguration und Parameter:** Feature-Schalter, Mandantenparameter, systemweite Defaults.
3. **UI- und Reportartefakte:** Feldbezeichnungen, Validierungstexte, Druck-/Exportformate.
4. **Datenstrukturbezogene Artefakte:** Datenmodelle, Constraints, Referenzen, historisierte Strukturen.
5. **Projektartefakte:** Tickets, Release Notes, Testfälle, Migrationsnotizen.
1. *Quellcode:* Implementierte Regeln, Berechtigungen, Statuslogik, Datenzugriffe.
2. *Konfiguration und Parameter:* Feature-Schalter, Mandantenparameter, systemweite Defaults.
3. *UI- und Reportartefakte:* Feldbezeichnungen, Validierungstexte, Druck- und Exportformate.
4. *Datenstrukturbezogene Artefakte:* Datenmodelle, Constraints, Referenzen, historisierte Strukturen.
5. *Projektartefakte:* Tickets, Release Notes, Testfälle, Migrationsnotizen.
Diese Artefakte sind nicht gleichwertig. Für Requirements-Rückgewinnung ist entscheidend, dass Belege in ihrer Aussagekraft bewertet und im Prozess sichtbar gemacht werden. Damit wird die fachliche Validierung gezielt auf risikoreiche oder unsichere Aussagen gelenkt.
Quellcode und Datenstrukturen liefern den belastbarsten Beleg für eine Regel, während Tickets und Release Notes vor allem den Anlass einer Änderung dokumentieren. Die fachliche Validierung wird gezielt auf risikoreiche oder unsichere Aussagen gelenkt.
#heading(level: 2)[Migrationsstrategie und spezifische Herausforderungen]
#heading(level: 3)[Zielbild der Modernisierung]
#heading(level: 3)[Ziel der Modernisierung]
Das Modernisierungsziel ist eine webbasierte Plattform, die plattformunabhängige Nutzung und modernere Betriebsmodelle unterstützt. Damit verschiebt sich der Schwerpunkt von einer rein funktionalen Betrachtung hin zu einem Zusammenspiel aus Funktionsäquivalenz und nicht-funktionalen Zielgrößen, insbesondere im Bereich Betrieb, Sicherheit und Änderbarkeit. Das Qualitätsmodell ISO/IEC 25010:2011 bietet hierfür eine etablierte Taxonomie, um solche Zielgrößen systematisch zu erfassen @iso25010_2011.
Ziel der Modernisierung ist eine webbasierte SaaS-Plattform, die sowohl Cloud- als auch On-Premise-Betrieb unterstützt. Neben der Funktionsäquivalenz zur bestehenden ERP-Suite stehen nicht-funktionale Anforderungen wie Skalierbarkeit, Mandantenfähigkeit und KI-Anbindung im Vordergrund. Konkret leiten sich für das Fallunternehmen die folgenden Anforderungen ab:
Aus Sicht des Fallunternehmens ist das Zielbild in drei Dimensionen zu konkretisieren:
/ Cloud- und On-Premise-Fähigkeit: Die Anwendung wird containerisiert ausgeliefert und ist damit unabhängig von einem bestimmten Infrastrukturanbieter. Neben dem zentralen SaaS-Betrieb bleibt eine Einzelinstallation beim Kunden möglich.
/ Multi-Tenant-Fähigkeit: Mehrere Mandanten teilen sich eine gemeinsame Datenbank, sodass auch kleinere Kunden ohne vollen Installationsaufwand bedient werden können. Für größere Kunden ist weiterhin eine Einzelinstallation vorgesehen.
/ Datenbanksystem-Unabhängigkeit: Geschäftslogik und Abfragen werden möglichst vollständig im Code abgebildet und nicht im DBMS. Damit sinken Migrationsaufwände, und im Multi-Tenant-Betrieb lassen sich günstige oder kostenlose Datenbanksysteme einsetzen.
/ Skalierbarkeit: Das Backend ist über Microservices und Load-Balancing horizontal skalierbar, um auch bei größeren Kunden performant zu bleiben.
/ KI-Fähigkeit: Schnittstellen zu KI-Systemen (z. B. MCP-Server, RAG-Embeddings) sind von Beginn an in der Architektur vorgesehen.
/ Web-Frontend: Eine responsive Web-Oberfläche ermöglicht die geräteherstellerunabhängige Nutzung.
/ Zentrale Datenverwaltung: Das Basissystem stellt eine eigenständige Oberfläche zur Benutzer- und Kundenverwaltung bereit und dient auch ohne ERP-Funktionalität als Basis für weitere Produkte der Firma.
- **Benutzeroberfläche und Interaktion:** Webbasierte Oberflächen, Rollen- und Rechtekonzepte, konsistente Navigation.
- **Betriebsmodell und Deployment:** Automatisierte Bereitstellung, Skalierung, Updatefähigkeit und Wartbarkeit über Releases.
- **Integrationen und Daten:** Stabilisierung von Schnittstellen, Datenmigration und Sicherstellung konsistenter Geschäftsobjekte.
Als Performance-Ziel wird ein Bereich von 5 bis 100 gleichzeitigen Nutzern pro Mandant festgelegt. Kleinere Installationen mit 1 bis 5 Nutzern sowie größere Installationen oberhalb von 100 Nutzern werden ebenfalls unterstützt, bleiben aber opportunistische Ziele.
#heading(level: 3)[Strategische Optionen und Abgrenzung]
Die Literatur zur Legacy-Modernisierung betont, dass Migrationen unterschiedliche Strategien annehmen können und dass eine bewusste Planung notwendig ist, um Risiken zu steuern @sneed1995planning @bisbal1997overview. In der Praxis reichen Optionen von minimal-invasiven Ansätzen (z. B. Rehosting) bis zur schrittweisen Neuimplementierung zentraler Funktionen. Für diese Arbeit ist weniger die vollständige Migrationsplanung Gegenstand, sondern die Frage, wie eine belastbare Requirementsbasis für die Umsetzung erzeugt wird.
Migrationsstrategien für Legacy-Software reichen von leichtgewichtigen Ansätzen wie Refactoring bis zur schrittweisen Neuimplementierung zentraler Funktionen. Gegenstand dieser Arbeit ist nicht die vollständige Migrationsplanung, sondern die Frage, wie eine belastbare Requirementsbasis für die Umsetzung erzeugt wird.
Die Abgrenzung des Beitrags lautet daher:
- Schwerpunkt ist die **Rückgewinnung und Strukturierung** von Requirements aus bestehenden Artefakten.
- Architektur- und Implementationsentscheidungen der Zielplattform werden nur soweit diskutiert, wie sie Requirements beeinflussen (z. B. Sicherheits- und Betriebsanforderungen).
- Die eigentliche technische Migration (Datenmigration, Refactoring im Detail, Release-Planung) wird als Rahmenbedingung verstanden und in späteren Kapiteln methodisch adressiert.
Schwerpunkt ist die *Rückgewinnung und Strukturierung* von Requirements aus bestehenden Artefakten. Architektur- und Implementierungsentscheidungen der Zielplattform werden nur insoweit diskutiert, als sie Requirements beeinflussen, etwa bei Sicherheits- und Betriebsanforderungen. Die technische Migration selbst, also Datenmigration, Refactoring im Detail, Reimplementierung oder Release-Planung, wird lediglich als Randbedingung mit betrachtet.
#heading(level: 3)[Spezifische Herausforderungen im Fallunternehmen]
Die Fallstudie weist mehrere Herausforderungen auf, die für die Methodik des Reverse Requirements Engineering unmittelbar relevant sind. Die folgenden Punkte bündeln die zentralen Problemfelder und leiten Anforderungen an das Vorgehen ab:
Aus dem Ist-Zustand ergeben sich mehrere Herausforderungen, die das Vorgehen des Reverse Requirements Engineering unmittelbar prägen:
/ Randfäll und Varianten: Ein erheblicher Teil des Systemumfangs steckt in implementierten Sonderfällen und Varianten. Diese sind im Code zwar nachvollziehbar, aber selten dokumentiert und zur Laufzeit auch jeweils nur einzeln zu analysieren da sie sich oft gegenseitig ausschließen.
/ Daten und Logische Redundanz: Historisch wurden zusätzliche Funktionen oft inmplementiert indem Code oder Masken hinzugefügt wurden ohne alten code zu verändern. Daher kommt es vor, dass die die gleiche Anforderung auf Unterschiedlichen Masken oder Berechnungspfaden mehrfach unterschiedliche implementiert wurde.
/ Implizite Geschäftsregeln: Geschäftslogik ist häufig als Prüf- und Statuslogik oder über Randbediungungen in Eingabefeldern realisiert. Eine zuordung zu anderen zusammenghörigen Regeln fällt hier schwer.
/ Kontextbasierte Logik: Funktionalitäten und Logik sind oft nur lose über historische Zuständen verbunden. Eine Regel kann daher nur bei Betrachtung eines vollständigen historischen Datensatzes im Kontext abgeleitet werden.
/ Nicht-funktionale Anforderungen: Mit dem Wechsel auf einen neuen Tech-Stack ist zu prüfen, welche der bisherigen nicht-funktionalen Anforderungen weiterhin Gültigkeit haben und welche neu definiert werden müssen. Annahmen zu Performance, Sicherheit oder Verfügbarkeit gelten unter veränderten Architektur- und Betriebsbedingungen nicht automatisch weiter.
- **Funktionsäquivalenz und Randfälle:** Ein großer Teil des Systemwertes liegt in korrekt implementierten Ausnahmen, Sonderfällen und Prozessvarianten. Diese sind im Code sichtbar, aber selten dokumentiert. Daraus folgt eine hohe Priorität für Traceability und Validierung.
- **Implizite Geschäftsregeln:** Geschäftslogik ist häufig als Prüf- und Statuslogik realisiert. Ohne strukturierte Extraktion besteht das Risiko, dass Regeln in der Neuimplementierung vereinfacht oder falsch interpretiert werden.
- **Datenzentrierte Domäne:** ERP-Funktionalität ist eng mit Datenobjekten, Relationen und historisierten Zuständen gekoppelt. Anforderungen müssen daher nicht nur UI-nah formuliert werden, sondern auch als Daten- und Integritätsanforderungen.
- **Nicht-funktionale Anforderungen rücken in den Vordergrund:** Mit einer webbasierten Zielplattform steigen Anforderungen an Sicherheitsmechanismen, Verfügbarkeit, Rollout-Fähigkeit und Observability. Studien zu Security-Aspekten in Legacy-to-Cloud-Migrationen zeigen, dass Identitätsmanagement, Datenflusskontrolle und Compliance wiederkehrende Kernprobleme sind @security2014legacycloud.
- **Organisatorische Randbedingungen:** Ressourcen für manuelle Analyse sind begrenzt und hängen von erfahrenen Mitarbeitern ab. Daraus folgt die Notwendigkeit, Analysearbeit skalierbar zu machen und Ergebnisse reproduzierbar zu erzeugen.
#heading(level: 3)[Konsequenzen für das Vorgehen in dieser Arbeit]
Aus den genannten Herausforderungen ergeben sich konkrete Anforderungen an das in Kapitel 4 entwickelte Vorgehen:
Aus den Herausforderungen ergeben sich vier konkrete Anforderungen an das Vorgehen. Zunächst gilt eine *Belegpflicht*. Jede extrahierte Anforderung muss auf ein Artefakt wie eine Datei, ein Modul, ein Datenobjekt oder einen UI-Text zurückgeführt werden können. Aussagen, die nicht eindeutig aus Artefakten ableitbar sind, werden *explizit als Hypothesen markiert* und priorisiert validiert. Da Artefakte verteilt sind, ist eine *Segmentierung und Kontextsteuerung* notwendig, um Überinterpretation zu reduzieren. Schließlich ist eine fachliche Validierung durch einen *Human-in-the-loop* zwingend, da „plausible" Textausgaben kein hinreichender Beweis für fachliche Korrektheit sind.
1. **Belegpflicht und Nachvollziehbarkeit:** Jede extrahierte Anforderung muss auf Artefakte zurückgeführt werden können (Datei, Modul, Datenobjekt, UI-Text).
2. **Explizite Unsicherheitskennzeichnung:** Aussagen, die nicht eindeutig aus Artefakten ableitbar sind, müssen als Hypothesen markiert und priorisiert validiert werden.
3. **Segmentierung und Kontextsteuerung:** Da Artefakte verteilt sind, ist eine systematische Auswahl relevanter Kontexte notwendig, um Überinterpretation zu reduzieren.
4. **Human-in-the-loop:** Fachliche Validierung ist zwingend, da „plausible“ Textausgaben kein hinreichender Beweis für fachliche Korrektheit sind.
Damit schafft dieses Kapitel die Grundlage für die folgenden Abschnitte: Kapitel 4 beschreibt das methodische Vorgehen und die Claude-Code-basierte Durchfuehrung, Kapitel 5 dokumentiert die Ergebnisse der Versuche, und Kapitel 6 evaluiert die Eignung im Fallkontext der c-entron GmbH.
+5 -409
View File
@@ -2,419 +2,15 @@
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
// Fallback for standalone preview of this chapter (without global thesis style).
#show raw: set text(font: "DejaVu Sans Mono", size: 9.5pt, fill: luma(20))
#show raw.where(block: true): it => block(
width: 100%,
fill: luma(240),
stroke: 0.5pt + luma(190),
inset: 9pt,
radius: 4pt,
above: 0.8em,
below: 0.8em,
it,
)
]
#heading(level: 1)[Konzeption und methodisches Vorgehen (ca. 12 Seiten)]
#heading(level: 1)[Konzeption und methodisches Vorgehen]
Dieses Kapitel beschreibt die tatsaechlich durchgefuehrte Methodik mit Fokus auf Claude Code als zentralem Arbeitswerkzeug. Alle Informationen sind versuchsweise gebuendelt dargestellt, sodass pro Versuch die Konfiguration, die Prompts, die eingesetzten Tools und die resultierenden Artefakte geschlossen nachvollziehbar sind.
Dieses Kapitel beschreibt die Methodik, mit der die in Kapitel 1 beschriebenen Ziele und Forschungsleitfragen beantwortet werden sollen. Ausgangspunkt ist das methodische Design. Aus diesem Design leiten sich alle weiteren methodischen Entscheidungen ab. Vorausgegangene Proof-of-Concept-Läufe haben einzelne Aspekte des Vorgehens informell erprobt und das hier dargestellte Vorgehen geprägt, sie sind aber nicht Gegenstand der Auswertung. Die eigentliche Untersuchung wird in den folgenden Abschnitten geplant und in den folgenden Kapiteln durchgeführt und bewertet.
#heading(level: 2)[Claude Code als Werkzeug]
#include "04_konzeption_methodisches_vorgehen/04_01_methodisches_design.typ"
Claude Code wurde in dieser Arbeit als lokales Analysewerkzeug genutzt: ueber die CLI im Projektarbeitsverzeichnis und ueber die VS-Code-Einbindung. Die Arbeitslogik folgt einem schrittweisen Ausbau:
- Baseline nur mit Prompt + CLI (Versuch 01),
- Spezialisierung ueber Agenten-Dateien (Versuch 02),
- Erweiterung um MCP-Server fuer zusaetzliche Tool- und Datenzugriffe (Versuch 03).
Technisch wurde Claude Code entlang der offiziellen Dokumentation eingesetzt:
- Session-Start und Ausfuehrung ueber CLI (`claude`, `claude -p`),
- lokale IDE-Anbindung in VS Code,
- Einbindung externer MCP-Server ueber das `claude mcp`-Konzept,
- Nutzung des MCP-Scopes fuer projektspezifische Tool-Konfigurationen.
Die technische Einordnung stuetzt sich auf die offizielle Claude-Code-Dokumentation zu Quickstart, CLI-Nutzung, IDE-Integration und MCP sowie auf das Produktupdate zu Remote MCP @claudecode_quickstart_2026 @claudecode_cli_2026 @claudecode_ide_2026 @claudecode_mcp_2026 @anthropic_remote_mcp_2025.
Damit fungiert Claude Code in dieser Arbeit nicht nur als Chat-Interface, sondern als orchestrierender Agent-Laufzeitkontext fuer Prompting, rollenbasierte Agenten und MCP-basierte Toolaufrufe.
#heading(level: 2)[Versuch 01]
#heading(level: 3)[Allgemeine Beschreibung]
Versuch 01 bildet die Baseline ohne Agenten und ohne MCP. Ziel war eine erste formale Requirements-Extraktion direkt aus der Codebasis mit minimaler Tooling-Komplexitaet.
#heading(level: 3)[Konfiguration]
- Claude Code CLI lokal im Projektverzeichnis,
- Nutzung aus VS Code (integriertes Terminal),
- keine Agenten-Dateien,
- keine MCP-Server.
#heading(level: 3)[Verwendeter Prompt]
```text
Please analyze this software project and write a reuqirements specification according to modern standards.
```
#heading(level: 3)[Tools und Artefakte]
- Tooling: Claude Code CLI + VS Code Integration.
- Ergebnisfokus: formale Requirements-Spezifikation (StRS/SyRS/SwRS) mit hoher Strukturierungsdichte.
#heading(level: 3)[Beispielhafte Ergebnisanforderungen]
Quelle: `Versuche/Versuch 01/Ergebnisse/ISO29148_Complete_Requirements_Specification.md`
```text
### StR-001: Comprehensive Customer Account Management
**Statement**: The system shall provide comprehensive customer account management capabilities including contact information, relationship mapping, interaction history, and account hierarchy management.
```
```text
### SyR-001
The system SHALL implement a multi-layered architecture with clear separation of concerns.
```
#heading(level: 3)[Einordnung]
Die Baseline zeigt, dass bereits ohne Agenten/MCP belastbare, formal strukturierte Anforderungen erzeugbar sind. Gleichzeitig bleibt die Discovery-Breite begrenzt.
#heading(level: 2)[Versuch 02]
#heading(level: 3)[Allgemeine Beschreibung]
Versuch 02 fokussiert die ISO-29148-orientierte Konsolidierung. Dazu wurde Claude Code weiterhin lokal genutzt, jedoch um spezialisierte Agenten-Dateien erweitert.
#heading(level: 3)[Konfiguration]
- Claude Code CLI lokal + VS Code,
- agentenbasierte Spezialisierung ueber MD-Dateien in `Versuche/Versuch 02/Tools/agents/`,
- kein MCP-Fokus in diesem Lauf.
#heading(level: 3)[Verwendeter Prompt]
```text
Please analyze this software project and write a ISO 29148 compliant reuqirements specification.
Use Agents wherever possible.
```
#heading(level: 3)[Tools und Agenten]
Beispiele aus dem Versuchsordner:
- `iso29148-master-orchestrator-agent.md`
- `iso29148-stakeholder-agent.md`
- `iso29148-system-requirements-agent.md`
- `iso29148-software-requirements-agent`
#heading(level: 3)[Agentenbeispiel (Auszug, erste 100 Zeilen) - Versuch 02]
Quelle: `Versuche/Versuch 02/Tools/agents/iso29148-master-orchestrator-agent.md`
````md
# Enhanced ISO 29148 Master Orchestrator Agent with Milestone System
You are the Lead Requirements Analyst coordinating the complete ISO/IEC/IEEE 29148 requirements extraction with comprehensive documentation, quality assurance, and milestone-based execution control.
## Your Mission
Orchestrate a complete requirements analysis using all three ISO 29148 levels, ensuring consistency, completeness, and traceability. Create executive-level documentation and ensure all agents produce their complete documentation packages. **NEW**: Provide milestone-based pause/resume capabilities for long-running analyses.
## CRITICAL: Documentation Requirements
**You MUST ensure:**
1. Each agent creates their complete documentation package
2. You create the integrated master document
3. All work is saved to `/docs/requirements/`
4. Complete traceability is maintained
5. Executive dashboards and reports are generated
6. **NEW**: Milestone state is persisted for pause/resume functionality
7. VERIFY each agent has created their files before proceeding
## NEW: Milestone System Architecture
### Milestone Configuration
```json
{
"project_name": "[Project Name]",
"execution_id": "[UUID]",
"created_at": "[ISO DateTime]",
"milestones": {
"M0_SETUP": {
"name": "Project Analysis and Setup",
"status": "pending|in_progress|completed|failed",
"started_at": null,
"completed_at": null,
"dependencies": [],
"outputs": ["project_structure.json", "directory_setup.txt"]
},
"M1_STAKEHOLDER": {
"name": "Stakeholder Requirements Analysis",
"status": "pending",
"started_at": null,
"completed_at": null,
"dependencies": ["M0_SETUP"],
"outputs": [
"StRS_Complete.md",
"StRS_Summary.md",
"StRS_Traceability.csv",
"StRS_Diagrams.md",
"StRS_Evidence.md"
]
},
"M2_SYSTEM": {
"name": "System Requirements Analysis",
"status": "pending",
"started_at": null,
"completed_at": null,
"dependencies": ["M1_STAKEHOLDER"],
"outputs": [
"SyRS_Complete.md",
"SyRS_Summary.md",
"SyRS_API_Specification.yaml",
"SyRS_Architecture.md",
"SyRS_Interfaces.md",
"SyRS_Traceability.csv"
]
},
"M3_SOFTWARE": {
"name": "Software Requirements Analysis",
"status": "pending",
"started_at": null,
"completed_at": null,
"dependencies": ["M2_SYSTEM"],
"outputs": [
"SwRS_Complete.md",
"SwRS_CodeCatalog.md",
"SwRS_Algorithms.md",
"SwRS_DataModel.md",
"SwRS_TestSpecification.md",
"SwRS_Traceability.csv"
]
},
"M4_PATTERNS": {
"name": "Code Pattern Analysis",
"status": "pending",
"started_at": null,
"completed_at": null,
"dependencies": ["M3_SOFTWARE"],
"outputs": [
"Analysis_Complete.md",
"Pattern_Catalog.csv",
"Business_Rules.md",
"Validation_Rules.md",
"Security_Patterns.md",
"Performance_Patterns.md",
"Integration_Patterns.md"
]
},
"M5_INTEGRATION": {
"name": "Integration and Master Documentation",
"status": "pending",
"started_at": null,
"completed_at": null,
"dependencies": ["M1_STAKEHOLDER", "M2_SYSTEM", "M3_SOFTWARE", "M4_PATTERNS"],
````
Hinweis: Der Auszug endet nach Zeile 100; die Originaldatei umfasst 620 Zeilen und ist an dieser Stelle nicht zu Ende.
#heading(level: 3)[Beispielhafte Ergebnisanforderungen]
Quellen:
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Complete.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_Complete.md`
```text
**SyR-001**: The system SHALL implement a multi-layered architecture with clear separation of concerns.
**SyR-002**: The system SHALL implement the ILogic interface pattern with dual implementations.
```
```text
**SW-FUNC-001**: The software SHALL provide comprehensive account management functionality.
**SW-API-001**: The software SHALL provide comprehensive REST API.
```
#heading(level: 3)[Einordnung]
Versuch 02 lieferte die staerkste formale Konsolidierung (StRS/SyRS/SwRS, hohe Traceability), erwies sich fuer die Gesamtentdeckung jedoch als vergleichsweise rigide.
#heading(level: 2)[Versuch 03]
#heading(level: 3)[Allgemeine Beschreibung]
Versuch 03 erweitert das Vorgehen aus Versuch 02 um MCP-Server, um neben formaler Strukturierung vor allem die Discovery-Breite zu vergroessern (Use-Case-Fund, Gap-Analyse).
#heading(level: 3)[Konfiguration]
- Claude Code CLI lokal + VS Code,
- Agenten-Dateien in `Versuche/Versuch 03/Tools/Agents/`,
- MCP-Server gemaess Protokoll: Serena MCP, Windows-MCP (AutoIt-basiert), MSSQL MCP.
#heading(level: 3)[Verwendeter Prompt]
```text
Please analyze this software project and write a reuqirements specification according to modern standards.
Use Agents and MCP servers wherever possible.
Keep superflous texts to a minimum and concentrate on actual requirements.
```
#heading(level: 3)[Tools und Agenten]
Beispiele aus dem Versuchsordner:
- `centron-documentation-writer.md`
- `nhibernate-query-reviewer.md`
- `centron-code-reviewer.md`
- `webservice-developer.md`
#heading(level: 3)[Agentenbeispiel (Auszug, erste 100 Zeilen) - Versuch 03]
Quelle: `Versuche/Versuch 03/Tools/Agents/nhibernate-query-reviewer.md`
````md
---
name: nhibernate-query-reviewer
description: Reviews NHibernate queries and LINQ expressions for c-entron.NET. Detects N+1 queries, cartesian products, and compatibility issues. Use when writing complex queries or experiencing performance problems. Keywords: NHibernate, LINQ, query, performance, N+1, optimization, Fetch.
---
# NHibernate Query Reviewer Agent
> **Type**: Review / Analysis
> **Purpose**: Review database queries to ensure efficiency, proper structure, and compatibility with NHibernate's LINQ provider limitations.
## Agent Role
You are a specialized **NHibernate Query Reviewer** for the c-entron.NET solution, focused on query optimization and performance.
### Primary Responsibilities
1. **N+1 Detection**: Identify and fix lazy loading issues that cause multiple database roundtrips
2. **Performance Analysis**: Review queries for cartesian products, missing indexes, and inefficient patterns
3. **NHibernate Compatibility**: Ensure LINQ expressions translate correctly to SQL
4. **Best Practices**: Enforce soft delete filtering, eager loading strategies, and proper transaction usage
### Core Capabilities
- **N+1 Query Detection**: Identify lazy loading in loops causing performance degradation
- **Cartesian Product Prevention**: Detect multiple Fetch operations on collections
- **LINQ Compatibility**: Validate expressions work with NHibernate's LINQ provider
- **Optimization Recommendations**: Suggest Fetch, FetchMany, Future queries for better performance
- **Soft Delete Validation**: Ensure all queries filter IsDeleted records
## When to Invoke This Agent
This agent should be activated when:
- Complex LINQ queries are written
- Performance issues suspected with database access
- Need query optimization recommendations
- Validating NHibernate compatibility of LINQ expressions
- Reviewing data access code for N+1 problems
- Before committing database access code
**Trigger examples:**
- "Review this query for N+1 problems"
- "Optimize the GetAccountContracts query"
- "Check if this LINQ expression will work with NHibernate"
- "Why is my query slow?"
## Technology Adaptation
**IMPORTANT**: This agent adapts to c-entron.NET's NHibernate configuration.
**Configuration Source**: [CLAUDE.md](../../CLAUDE.md)
Before beginning work, review CLAUDE.md for:
- **ORM**: NHibernate 5.x with FluentNHibernate
- **Database**: SQL Server 2019+
- **Pattern**: Always filter !x.IsDeleted
- **Eager Loading**: Fetch/FetchMany for navigation properties
- **Future Queries**: Batch loading for multiple collections
- **Transactions**: Required for all modifications
## Instructions & Workflow
### Standard Procedure
1. **Load Relevant Lessons Learned** ⚠️ **IMPORTANT**
As a review and analysis agent, start by loading past lessons:
- Use Serena MCP `list_memories` to see available memories
- Use `read_memory` to load relevant past findings:
- `"lesson-query-*"` - Query optimization lessons
- `"pattern-nhibernate-*"` - NHibernate patterns
- `"lesson-performance-*"` - Performance findings
- Apply insights from past lessons throughout review
- This prevents repeating past N+1 mistakes
2. **Context Gathering**
- Review [CLAUDE.md](../../CLAUDE.md) for NHibernate patterns
- Use Serena MCP `find_symbol` to locate query implementations
- Use Serena MCP `find_referencing_symbols` to understand query usage
- Identify query complexity and data access patterns
3. **Query Analysis**
- Check for N+1 query patterns (lazy loading in loops)
- Verify soft delete filtering (!x.IsDeleted)
- Validate LINQ expression compatibility
- Look for cartesian products (multiple Fetch on collections)
- Check transaction usage for modifications
- **Apply insights from loaded lessons**
4. **Optimization**
- Suggest Fetch/FetchMany for eager loading
- Recommend Future queries for multiple collections
- Propose projection for limited data needs
- Identify missing indexes
- **Check recommendations against past patterns**
5. **Verification**
- Estimate performance impact
- Verify proposed optimizations don't introduce new issues
- Use `/optimize` command for additional suggestions
````
Hinweis: Der Auszug endet nach Zeile 100; die Originaldatei umfasst 284 Zeilen und ist an dieser Stelle nicht zu Ende.
#heading(level: 3)[Beispielhafte Ergebnis-Use-Cases]
Quelle: `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES.md`
```text
### 2. Click Counter Management (Usage-Based Billing)
**Purpose**: Retrieve current meter readings for click counter devices
**Use Cases**: Copy machines, printers, industrial equipment with usage meters
Method: LoadCounterAsync(List<int> contractsI3D)
```
```text
**Use Case**: Track counter reading trends, detect anomalies
Method: IAutomatedBillingLogic.GetCounterHistory(List<string> lstParam)
```
#heading(level: 3)[MCP-Server: Detaillierte Beschreibung]
#heading(level: 4)[MCP-Grundprinzip]
Model Context Protocol (MCP) definiert eine standardisierte Kopplung zwischen einem Host (hier: Claude Code), einem MCP-Client und einem oder mehreren MCP-Servern. Server stellen dabei typischerweise drei Artefaktarten bereit: `tools` (aufrufbare Funktionen), `resources` (lesbare Kontexte) und `prompts` (wiederverwendbare Prompt-Bausteine). Dieses Modell wurde in Versuch 03 genutzt, um ueber den reinen Repository-Kontext hinaus weitere Wissens- und Interaktionskanaele einzubinden @mcp_intro_2026.
#heading(level: 4)[Serena MCP]
Serena ist ein MCP-Server fuer semantische Code-Retrieval- und Editieroperationen auf Symbol-Ebene (z. B. `find_symbol`, `find_referencing_symbols`, `insert_after_symbol`). Im Unterschied zu rein textbasierter Suche werden Codeobjekte (Klassen, Methoden, Referenzen) strukturell adressiert. In Versuch 03 wurde Serena vor allem fuer gezielte Modulnavigation und die persistenten Memory-Notizen zwischen Analyseiterationen eingesetzt @serena_mcp_2026 @mcp_servers_repo_2026.
#heading(level: 4)[Windows-MCP (AutoIt-basiert)]
Der im Protokoll genannte Windows-MCP-Ansatz (AutoIt-basiert) realisiert Desktop-Automatisierung ueber MCP. Laut Projektbeschreibung kapselt der Server AutoIt-Funktionen als MCP-Tools und bietet zusaetzlich Ressourcen (Dateizugriff, Screenshots) sowie Prompt-Templates fuer typische Automationsaufgaben. Fuer die Fallstudie ist das relevant, weil GUI-basierte Pfade (Dialoge, Formulare, visuelle Workflows) nicht nur aus Quellcode, sondern auch aus Interaktionsablaeufen rekonstruiert werden koennen @windows_mcp_autoit_2026.
#heading(level: 4)[MSSQL MCP]
MSSQL MCP ermoeglicht den kontrollierten Zugriff auf Microsoft SQL Server ueber MCP. Typische Funktionen sind Tabellenauflistung, Schema-Inspektion, Lesen von Inhalten und kontrollierte SQL-Ausfuehrung. Die dokumentierten Security-Hinweise betonen Least-Privilege-Berechtigungen, restriktive Verbindungskonfigurationen und Logging. In Versuch 03 wurde dieser Zugriff fuer die funktionale Absicherung von Datenmodellen und Use-Case-Hypothesen genutzt @mssql_mcp_2026.
#heading(level: 3)[Einordnung]
Durch die MCP-Erweiterung konnte Versuch 03 die funktionale Breite deutlich steigern und einen grossen Dokumentations-Gap sichtbar machen. Gegenueber Versuch 02 sinkt dabei der formale ISO-Fokus, was fuer Discovery jedoch methodisch beabsichtigt war.
#heading(level: 2)[Quellenhinweis]
Die fuer dieses Kapitel genutzten Webquellen zu Claude Code und MCP-Servern sind im Literaturverzeichnis als Online-Quellen erfasst; die inhaltliche Referenzierung erfolgt direkt im Text der Abschnitte zu Versuch 03.
#include "04_konzeption_methodisches_vorgehen/04_02_versuchsdesign.typ"
#include "04_konzeption_methodisches_vorgehen/04_03_evaluation_und_absicherung.typ"
@@ -0,0 +1,142 @@
#import "@preview/cetz:0.4.2"
#set text(lang: "de")
#heading(level: 2)[Werkzeug-Grundlagen: Agentische CLIs, Agenten, MCP und lokale Inferenz]
Bevor der eigentliche Versuchsaufbau beschrieben wird, werden die zentralen Werkzeug-Begriffe geklärt, an denen die spätere Versuchsreihe variiert. Sie sind nicht nur Hilfsmittel der Durchführung, sondern definieren genau die Stellschrauben, die in den Versuchen V1 bis V3 systematisch hinzugenommen werden. Die folgende Darstellung beschränkt sich auf das für diese Arbeit relevante Maß und ersetzt keine vollständige Werkzeugdokumentation. Das Zusammenspiel der vier Bausteine ist in @abb_werkzeug_grundlagen skizziert.
/ Agentische CLIs: Eine LLM CLI ist ein Kommandozeilen-Werkzeug, das einem LLM den direkten Zugriff auf die lokale Arbeitsumgebung erlaubt. Es kann nicht nur online Artefakte erzeugen, sondern auch lokale Dateien lesen und bearbeiten sowie Shell-Befehle ausführen oder lokal installierte Tools aufrufen. Innerhalb einer Ausführungsschleife entscheidet das LLM selbst, welches Werkzeug es auf dem lokalen Computer aufrufen möchte. Die CLI stellt dafür lediglich die Laufzeitumgebung und den Zugriff auf die lokalen Werkzeuge bereit. In dieser Arbeit kommen Claude Code , die Codex-CLI sowie die Qwen Code CLI zum Einsatz.
/ Agenten und Agentendateien: LLM Agenten sind rollenspezialisierte Konfigurationen (Prompts), die innerhalb einer agentischen CLI als Sub-Prozesse aufgerufen werden. Sie werden typischerweise als Markdown-Dateien abgelegt und enthalten drei Bestandteile: einen Auftrag (was tut der Agent), eine eigene Systemanweisung (wie tut er es) und ein begrenztes Toolset (womit darf er es tun). Die aufrufende CLI startet einen solchen Agenten mit eigenem Kontextfenster, sodass dessen Zwischenergebnisse den Hauptkontext nicht überfluten.
/ Model Context Protocol (MCP): Das Model Context Protocol ist ein 2024 von Anthropic veröffentlichter offener Standard zur Anbindung externer Werkzeuge und Datenquellen an LLM-Clients @claudecode_mcp_2026. Ein MCP-Server kapselt konkrete Fähigkeiten, etwa Symbol-Navigation im Quellcode, Datenbankzugriff oder GUI-Manipulation, hinter einer einheitlichen Schnittstelle. Ein MCP-Client wie Claude Code entdeckt die angebotenen Werkzeuge zur Laufzeit und ruft sie kontrolliert auf, um ein deterministischer Ergebnis zu erhalten, anstatt den Aufruf direkt durch das LLM zu generieren. So stellt etwa ein MCP-Server für lesenden Datenbankzugriff eine fest definierte SQL-Schnittstelle bereit, über die das LLM Tabellenstrukturen und Datensätze reproduzierbar abruft, anstatt sie aus dem allgemeinen Modellwissen zu rekonstruieren.
/ Lokale Inferenz-Runtimes: Eine lokale Inferenz-Runtime ist eine Software, die LLMs auf eigener Hardware ausführt und ihre Funktion über einen OpenAI-kompatiblen HTTP-Endpunkt bereitstellt. In dieser Arbeit kommt LM Studio zum Einsatz, das Modelle wie Qwen 3.5 Coder lokal ausführen kann.
#figure(
cetz.canvas({
import cetz.draw: *
let draw-box(x, y, w, h, label) = {
rect(
(x - w/2, y - h/2),
(x + w/2, y + h/2),
stroke: black + 0.6pt, fill: luma(240),
)
content((x, y), text(size: 9pt, weight: "bold")[#label])
}
let draw-arrow(from, to) = {
line(from, to, mark: (end: ">"), stroke: black + 0.6pt)
}
let h-half = 0.4
let gap = 0.15
draw-box(0, 5, 2.6, 0.8, "Benutzer")
draw-box(0, 3.5, 4.0, 0.8, "Agentische CLI")
draw-box(5, 3.5, 4.0, 0.8, "LLM (Cloud / LM Studio)")
draw-box(-4, 1, 3.6, 0.8, "Agentendateien")
draw-box(4, 1, 3.6, 0.8, "MCP-Server")
draw-box(0, -1, 3.6, 0.8, "Codebasis")
draw-box(4, -1, 3.6, 0.8, "Datenbank")
draw-arrow((0, 5 - h-half - gap), (0, 3.5 + h-half + gap))
line(
(0 + 4.0/2 + gap, 3.5),
(5 - 4.0/2 - gap, 3.5),
mark: (start: ">", end: ">"),
stroke: black + 0.6pt,
)
draw-arrow((0, 3.5 - h-half - gap), (-4, 1 + h-half + gap))
draw-arrow((0, 3.5 - h-half - gap), (4, 1 + h-half + gap))
draw-arrow((0, 3.5 - h-half - gap), (0, -1 + h-half + gap))
draw-arrow((4, 1 - h-half - gap), (4, -1 + h-half + gap))
}),
caption: [Hierarchie der Werkzeuge. Der Anwender ruft die agentische CLI auf. Diese übergibt das initiale Prompt und das verfügbare Toolset an das LLM und erhält dessen Antworten samt Tool-Aufrufen zurück. Die CLI führt die vom LLM erbetenen Aufrufe aus. Die Codebasis ist direkt über die CLI zugänglich. Der Zugriff auf die Datenbank erfolgt ausschließlich über einen MCP-Server.],
) <abb_werkzeug_grundlagen>
#heading(level: 2)[Methodisches Design im Überblick]
Das Vorgehen ist entlang der vier Forschungsleitfragen aus Kapitel 1 strukturiert. Diese werden im Folgenden mit Frage 1 (Steuerung und Reproduzierbarkeit), Frage 2 (KI-Extraktion und Stakeholder-Input), Frage 3 (Qualitätsbewertung) und Frage 4 (Chancen, Grenzen und Risiken) bezeichnet. Aus jeder Leitfrage folgt unmittelbar eine Datenquelle und ein Auswertungsweg. Damit ist sichergestellt, dass die methodischen Bausteine nicht nachträglich auf die Fragen abgebildet werden, sondern aus ihnen hervorgehen.
#figure(
cetz.canvas({
import cetz.draw: *
let stages = (
(y: 4.0, label: "Codebasis", q: none),
(y: 2.0, label: "KI-Extraktion", q: (num: "Frage 1", text: "Welche Steuerungsmechanismen und Kontrollpunkte sind notwendig, um LLMs reproduzierbar einzusetzen?")),
(y: 0.0, label: "Strukturierung", q: (num: "Frage 2", text: "Welche Anforderungen lassen sich aus Code extrahieren, welche müssen über Interviews ergänzt werden?")),
(y: -2.0, label: "Validierung", q: (num: "Frage 3", text: "Wie beurteilen Fachexperten Vollständigkeit, Verständlichkeit und Nützlichkeit der KI-Ergebnisse?")),
(y: -4.0, label: "Bewertung", q: (num: "Frage 4", text: "Welche Effizienzgewinne, Limitierungen und Risiken sind realistisch und müssen adressiert werden?")),
)
let box-half-w = 1.5
let box-half-h = 0.4
let stage-x = -5.0
let arrow-gap = 0.1
for s in stages {
rect(
(stage-x - box-half-w, s.y - box-half-h),
(stage-x + box-half-w, s.y + box-half-h),
stroke: black + 0.6pt, fill: luma(240),
)
content((stage-x, s.y), text(size: 9pt, weight: "bold")[#s.label])
}
for i in range(stages.len() - 1) {
let s1 = stages.at(i)
let s2 = stages.at(i + 1)
line(
(stage-x, s1.y - box-half-h - arrow-gap),
(stage-x, s2.y + box-half-h + arrow-gap),
mark: (end: ">"),
stroke: black + 0.6pt,
)
}
let q-x = stage-x + box-half-w + 0.6
for s in stages {
if s.q != none {
content(
(q-x, s.y),
anchor: "west",
box(
width: 9cm,
text(size: 9pt)[*#s.q.num:* #s.q.text],
),
)
}
}
}),
caption: [Methodisches Design im Überblick. Die vertikale Sequenz zeigt den Ablauf von der Codebasis bis zur Bewertung. Pro Phase ist die zugeordnete Forschungsleitfrage angeben.],
) <abb_forschungsdesign>
Der untersuchte Prozess folgt einer durchgehenden Kette von der Codebasis bis zum Requirement. Auf der Codebasis setzt eine KI-gestützte Extraktion auf. Die Ergebnisse werden in eine konsistente Spezifikationsform überführt und durch Fachexperten validiert. Die abschließende Bewertung erfolgt mit Hilfe vordefinierter Qalitätskriterien.
Aus diesem Ablauf ergeben sich drei methodische Module, die in den folgenden Abschnitten ausgearbeitet werden. Erstens der *kontrollierte Tooling-Vergleich*. Es ist eine Versuchsreihe vorgesehen, die auf derselben Codebasis und mit demselben Grundprompt arbeitet und sich gezielt nur in einzelnen Werkzeugkomponenten unterscheidet. Die konkrete Anzahl und Zuschnitt der Versuche werden im Untersuchungsdesign festgelegt. Zweitens die *strukturierte Stakeholder-Validierung*. Jede extrahierte Anforderung soll durch Domänenexperten geprüft, anhand einer Likert-Skala bewertet und durch halbstrukturierte Interviews ergänzt werden. Drittens die *RE-Qualitätsbewertung*. Die Bewertungskriterien werden vor der Durchführung definiert, sodass eine nachträgliche Kriterienwahl ausgeschlossen ist. Die Bewertung schließt neben der klassischen RE-Qualität auch die Migrations- und Konsolidierungsperspektive ein, also die Frage, ob eine extrahierte Anforderung im Zielsystem in dieser Form erhalten bleiben oder mit anderen zusammengeführt werden sollte.
#heading(level: 2)[Der RRE-Prozess als Rahmen]
Das in dieser Arbeit untersuchte Vorgehen folgt der in Kapitel 2 hergeleiteten siebenstufigen Methodenkette für Reverse Requirements Engineering. Die Schritte bauen aufeinander auf und decken den Weg von der ersten Abgrenzung des Untersuchungsgegenstands bis zur Validierung der gewonnenen Anforderungen ab.
1. *Scope und Domänenabgrenzung:* Auswahl relevanter Module, Datenobjekte und Prozesse.
2. *Artefakterhebung:* Quellcode, Konfiguration, UI-Texte, Datenbankschemata, Schnittstellenbeschreibungen und Change-Historie.
3. *Technische Analyse:* Struktur- und Abhängigkeitsanalyse sowie Identifikation von Kernkomponenten, Regeln und Integrationspunkten.
4. *Semantische Interpretation:* Ableitung fachlicher Aussagen aus technischen Implementierungen.
5. *Formalisierung:* Überführung in klare, testbare Anforderungen mit Kontext, Vorbedingung und Ergebnis.
6. *Traceability-Anreicherung:* Verknüpfung jedes Requirements mit Artefaktbelegen.
7. *Validierung:* Review durch Fachexperten und Abgleich mit Laufzeitverhalten oder Tickets.
In dieser Arbeit werden lediglich Schritt 1 und Schritt 7 manuell durchgeführt. Die dazwischenliegenden Schritte 2 bis 6 sollen KI-gestützt automatisiert werden. Der Untersuchungsschwerpunkt liegt damit nicht nur auf der Anforderungsbeschreibung, sondern vor allem auch auf der zuverlässigen Erzeugung dieser Beschreibung durch ein LLM.
Damit das Vorgehen belastbar bleibt, sind in jedem Versuchsdurchlauf drei Eigenschaften sicherzustellen:
/ Belegpflicht: Jede extrahierte Anforderung muss auf ein konkretes Artefakt wie eine Datei, ein Modul, ein Datenobjekt oder einen UI-Text zurückführbar sein.
/ Explizite Hypothesenmarkierung: Aussagen, die nicht eindeutig aus Artefakten ableitbar sind, werden als Hypothesen markiert und gesondert validiert.
/ Human-in-the-loop: Die fachliche Validierung durch Domänenexperten ist nicht optional. Plausibel formulierte LLM-Ausgaben sind kein hinreichender Beweis für sachliche Korrektheit.
Mit der Festlegung der Schrittfolge, der Aufteilung zwischen Mensch und KI sowie den drei Pflicht-Eigenschaften ist der Bezugsrahmen geklärt, in dem die folgenden Abschnitte ihre Detailfragen verorten.
@@ -0,0 +1,189 @@
#import "@preview/cetz:0.4.2"
#set text(lang: "de")
#heading(level: 2)[Auswahl des LLM]
Die Wahl des konkret eingesetzten LLM bestimmt maßgeblich, welche Steuerungsmechanismen praktisch umgesetzt werden können und wie reproduzierbar die Ergebnisse erzeugt werden. Aus diesem Grund wird die Werkzeugauswahl nicht implizit vorausgesetzt, sondern entlang der fünf Kriterien aus der Zielsetzung begründet. Diese Kriterien sind Kontextfenster, Codeverständnis, Steuerbarkeit, Kosten und Datenschutz.
Zur Auswahl stehen vier aktuell verfügbare Optionen. Anthropic Claude wird über die CLI-Variante Claude Code eingebunden, die agentisches Arbeiten und MCP-Integration nativ unterstützt. OpenAI bietet mit GPT-5 und der Codex-CLI eine vergleichbare agentische Schnittstelle. Als weitere Optionen kommen Qwen 3.5 Coder mit der Möglichkeit zur lokalen Ausführung ohne Cloud-Versand sowie DeepSeek R1 als cloudbasierte Alternative infrage.
#figure(
table(
columns: (1.4fr, 1fr, 1fr, 1fr, 1fr),
align: (left, left, left, left, left),
stroke: 0.4pt,
[*Kriterium*], [*Claude (Claude Code)*], [*GPT-5 (Codex)*], [*DeepSeek R1 (Cloud)*], [*Qwen 3.5 Coder (lokal)*],
[Kontextfenster], [bis 1 M Tokens], [bis 400 k Tokens], [bis 128 k Tokens], [bis 256 k Tokens],
[Codeverständnis], [hoch], [hoch], [hoch], [hoch],
[Steuerbarkeit (Agenten, MCP)], [nativ], [über Codex-CLI], [über API], [über Qwen Code CLI],
[Kosten], [API-Abrechnung], [API-Abrechnung], [API-Abrechnung], [Eigenbetrieb],
[Datenschutz], [Cloud-Versand], [Cloud-Versand], [Cloud-Versand], [On-Premise],
),
caption: [Vergleich der LLM-Optionen entlang der fünf Auswahlkriterien.],
) <tab_llm_vergleich>
Für diese Arbeit fällt die Entscheidung auf Claude Code als primäres Werkzeug. Ausschlaggebend sind das große Kontextfenster, die native Unterstützung von Agenten und MCP-Servern sowie eine offen dokumentierte CLI für reproduzierbare Aufrufe. Die kostenseitigen und datenschutzrechtlichen Nachteile gegenüber lokalen Modellen werden durch gezielte Konfigurationsmaßnahmen adressiert. Qwen 3.5 Coder und DeepSeek R1 werden für einen optionalen LLM-Querschnitt offengehalten, sind aber nicht das primäre Werkzeug. Qwen 3.5 Coder wird dabei lokal über LM Studio als Inferenz-Runtime betrieben. Die Anbindung erfolgt über den OpenAI-kompatiblen HTTP-Endpunkt von LM Studio, an den die Qwen Code CLI und gegebenenfalls MCP-Clients adressiert werden. Damit bleibt der Versuchsaufbau ohne Cloud-Versand und ohne Übertragung kundenbezogener Codeausschnitte an externe Anbieter.
#heading(level: 2)[Versuchsaufbau]
Der Versuchsaufbau folgt einer schrittweise aufbauenden Vergleichslogik. Versuch 1 bis 3 bilden den Kern der Reihe und werden ausschließlich auf Claude Code durchgeführt. Ausgehend von einer Baseline werden in jedem weiteren Versuch zusätzliche Werkzeugkomponenten hinzugefügt, sodass der Effekt jeder Komponente isoliert beobachtbar ist. Alle drei Versuche arbeiten auf derselben Codebasis und mit demselben Grundprompt. Variiert wird ausschließlich die Werkzeugkonfiguration. Der optionale Versuch 4 kehrt die Logik um. Die in Versuch 1 bis 3 wirksamste Konfiguration wird fixiert und auf den drei alternativen Modellen wiederholt, um modellabhängige von werkzeugabhängigen Effekten trennen zu können.
/ Versuch 1 (Baseline, Prompt-only): Reine Prompt-Steuerung ohne Agentendateien und ohne externe Tools. Die Hypothese lautet, dass eine formal strukturierte Anforderungsmenge bereits ohne Spezialisierung erreichbar ist, allerdings mit begrenzter Discovery-Breite und ohne dynamische Code- oder Datenbeobachtung.
/ Versuch 1b (Baseline mit werkzeugeigenen Agenten): Wie Versuch 1, jedoch mit den Subagenten, die das eingesetzte Werkzeug von sich aus mitbringt. Diese Subagenten sind keine Agentendateien im Sinne von Versuch 2, sondern eine Funktion der CLI. Der Versuch trennt damit den Effekt werkzeugeigener Delegation vom Effekt rollenspezialisierter Agentendateien. Die Hypothese lautet, dass die Delegation die Discovery-Breite erhöht, zugleich aber die Streuung zwischen Läufen deutlich vergrößert, weil das Werkzeug die Zerlegung der Analyse selbst wählt.
/ Versuch 2 (Spezialisierung über Agenten): Wie Versuch 1, ergänzt um rollenspezialisierte Agentendateien für Stakeholder-Analyse, System-Requirements, Software-Requirements und einen ISO-29148-Orchestrator. Die Hypothese lautet, dass Spezialisierung die Strukturierungstiefe und die Normkonformität erhöht, ohne die Discovery-Breite signifikant zu verschlechtern.
/ Versuch 3 (Toolzugriff über MCP-Server): Wie Versuch 2, ergänzt um strukturierten Tool-Zugriff über MCP. Vorgesehen sind drei Server für Symbol-Navigation auf Code-Ebene, für Datenbank-Inspektion auf Schema- und Datensatzebene sowie optional für GUI-Beobachtung. Die Hypothese lautet, dass strukturierter Tool-Zugriff die Discovery-Breite vergrößert und zuvor undokumentierte Use Cases sichtbar macht, allerdings zu Lasten erhöhter Steuerungskomplexität.
/ Versuch 4 (optional, LLM-Querschnitt): Die in den ersten drei Versuchen wirksamste Konfiguration wird auf allen drei alternativen Modellen wiederholt: GPT-5 über die Codex-CLI, DeepSeek R1 über die Cloud-API sowie Qwen 3.5 Coder lokal über LM Studio. Wo einzelne Werkzeugkomponenten in der jeweiligen Umgebung nicht eins zu eins verfügbar sind, wird ein funktional äquivalenter Ersatz gewählt und dokumentiert. Ziel ist eine Einschätzung, in welchem Maße die in Versuch 1 bis 3 beobachteten Effekte modellabhängig oder werkzeugabhängig sind.
#figure(
table(
columns: (auto, 1fr, 1.4fr),
align: (left + top, left + top, left + top),
stroke: 0.4pt,
[*Versuch*], [*Werkzeugkonfiguration*], [*Hypothese*],
[V1 Baseline],
[
- Prompt-only
- keine Agentendateien
- keine externen Tools
],
[
- Formal strukturierte Spezifikation erreichbar
- Discovery-Breite begrenzt
],
[V1b Werkzeug-Agenten],
[
- Wie V1
- werkzeugeigene Subagenten zugelassen
- keine Agentendateien
],
[
- Höhere Discovery-Breite
- Deutlich größere Streuung zwischen Läufen
],
[V2 Agenten],
[
- Wie V1b
- rollenspezialisierte Agentendateien
],
[
- Höhere Strukturierungstiefe und Normkonformität
- Vergleichbare Discovery-Breite
],
[V3 MCP-Tools],
[
- Wie V2
- MCP-Server für Code
- MCP-Server für Datenbank
- optional MCP-Server für GUI
],
[
- Größere Discovery-Breite
- Höhere Steuerungskomplexität
],
[V4 (optional)],
[
- beste Konfiguration aus V1–V3
- GPT-5 über Codex-CLI
- DeepSeek R1 über Cloud-API
- Qwen 3.5 Coder lokal über LM Studio
],
[- Trennung modell- gegenüber werkzeugabhängiger Effekte],
),
caption: [Übersicht der geplanten Versuche mit Werkzeugkonfiguration und Arbeitshypothese.],
) <tab_versuchsreihe>
#heading(level: 3)[Iteratives Vorgehen innerhalb eines Versuchs]
Innerhalb jedes Versuchs läuft ein iterativer Loop ab, in dem der Prompt schrittweise verfeinert wird. Der Begriff Iteration bezeichnet in dieser Arbeit ausschließlich diesen inneren Loop. Der schrittweise Aufbau über V1, V2 und V3 wird hingegen als Versuchsreihe bezeichnet.
Der Ablauf folgt fünf Schritten. Ausgangspunkt ist ein Initial-Prompt. In Versuch 1 wird dieser zu Versuchsbeginn neu formuliert. In Versuch 2 und Versuch 3 wird stattdessen der finale Prompt des vorhergehenden Versuchs übernommen und vor dem ersten Lauf an die neu hinzukommende Werkzeugkomponente angepasst (Agentendateien beziehungsweise MCP-Server). Der Initial-Prompt wird gegen die Codebasis ausgeführt. Anschließend begutachtet der Autor die Ausgabe stichprobenartig auf offensichtliche Lücken, formale Probleme oder fehlende Belege. Auf Basis dieser Begutachtung wird der Prompt mit dokumentiertem Änderungsgrund angepasst und erneut ausgeführt. Der Loop wiederholt sich, bis der Autor das Ergebnis als ausreichend einstuft.
Diese innere Begutachtung ersetzt nicht die Stakeholder-Validierung. Die Stakeholder-Validierung greift erst, wenn der Autor den Versuch als abgeschlossen erklärt. Ihr Urteil bezieht sich ausschließlich auf das End-Set des Versuchs. Damit fließt der Validatoren-Aufwand nicht in jede Zwischenversion ein.
Jede Prompt-Version wird im Versuchsordner mit Zeitstempel und kurzer Notiz zum Änderungsgrund abgelegt, sodass die Iterations-Historie reproduzierbar bleibt und später als Lerneffekt diskutiert werden kann. Der Ablauf ist in @abb_versuchsablauf zusammengefasst.
#figure(
cetz.canvas({
import cetz.draw: *
let draw-box(x, y, w, h, label, dashed: false) = {
rect(
(x - w/2, y - h/2),
(x + w/2, y + h/2),
stroke: if dashed { (dash: "dashed", thickness: 0.6pt, paint: black) } else { black + 0.6pt },
fill: luma(240),
)
content((x, y), text(size: 9pt, weight: "bold")[#label])
}
let draw-mini-box(x, y, w, h, label) = {
rect(
(x - w/2, y - h/2),
(x + w/2, y + h/2),
stroke: black + 0.4pt,
fill: luma(248),
)
content((x, y), text(size: 8pt)[#label])
}
let draw-arrow(from, to) = {
line(from, to, mark: (end: ">"), stroke: black + 0.6pt)
}
let h-half = 0.4
let mh-half = 0.25
let gap = 0.1
let top-y = 5
let versuche = (
(x: -5, label: "V1 Baseline"),
(x: -1.5, label: "V2 Agenten"),
(x: 2, label: "V3 MCP-Tools"),
)
for v in versuche {
draw-box(v.x, top-y, 3.0, 0.8, v.label)
}
draw-box(5.5, top-y, 3.0, 0.8, "V4 (optional)", dashed: true)
draw-arrow((-5 + 1.5, top-y), (-1.5 - 1.5, top-y))
draw-arrow((-1.5 + 1.5, top-y), ( 2 - 1.5, top-y))
line(
(2 + 1.5, top-y), (5.5 - 1.5, top-y),
mark: (end: ">"),
stroke: (dash: "dashed", thickness: 0.6pt, paint: black),
)
for v in versuche {
let cx = v.x
let py = 3.5
let ly = 2.5
let by = 1.5
draw-mini-box(cx, py, 1.7, 0.5, "Prompt")
draw-mini-box(cx, ly, 1.7, 0.5, "Lauf")
draw-mini-box(cx, by, 1.9, 0.5, "Begutachtung")
draw-arrow((cx, top-y - h-half - gap), (cx, py + mh-half + gap))
draw-arrow((cx, py - mh-half - gap), (cx, ly + mh-half + gap))
draw-arrow((cx, ly - mh-half - gap), (cx, by + mh-half + gap))
let lx = cx + 1.25
line((cx + 0.95, by), (lx, by), stroke: black + 0.6pt)
line((lx, by), (lx, py), stroke: black + 0.6pt)
line((lx, py), (cx + 0.85, py),
mark: (end: ">"), stroke: black + 0.6pt)
content((lx + 0.25, (py + by) / 2), text(size: 7pt, fill: luma(80))[nein])
}
}),
caption: [Zustandsgraph des Versuchsablaufs. Die Versuche bauen schrittweise aufeinander auf, V4 ist optional. Der finale Prompt eines Versuchs wird zu Beginn des Folgeversuchs an die neu hinzukommende Werkzeugkomponente angepasst und dient dann als Startprompt. Innerhalb jedes Versuchs verfeinert ein Iterations-Loop den Prompt: Lauf → Begutachtung durch den Autor → bei „nein" Prompt-Anpassung, bei „ja" Übergang zum nächsten Versuch.],
) <abb_versuchsablauf>
Abweichend von dieser Aufteilung wurde ein Teil des Modellvergleichs bereits innerhalb von Versuch 1 durchgeführt. Nachdem sich in den Wiederholungsläufen eine unerwartet große Streuung zeigte, wurde derselbe Prompt unter sonst identischer Bedingung zusätzlich mit zwei weiteren Modellen ausgeführt, um zu prüfen, ob diese Streuung modellabhängig ist. Diese Läufe liegen deshalb im Versuchsordner von Versuch 1 und nicht in Versuch 4. Sie ersetzen den dort geplanten LLM-Querschnitt nicht, da sie nur die Baseline-Konfiguration abdecken und nicht die in Versuch 1 bis 3 wirksamste Konfiguration. Ihr Zweck ist die Absicherung der Varianzaussage, nicht der Werkzeugvergleich.
Konstanten und Variablen sind in jedem Versuch klar dokumentiert. In Versuch 1 bis 3 umfassen die Konstanten Codebasis, Iterations-Vorgehen, Modellfamilie, Validierungsstichprobe und Bewertungskriterien. Variabel sind die Werkzeugkonfiguration und der Startprompt jedes Versuchs. Versuch 1 startet mit einem zu Versuchsbeginn neu formulierten Prompt. Versuch 2 und Versuch 3 übernehmen jeweils den finalen Prompt des vorhergehenden Versuchs und passen ihn zu Beginn an die neu hinzukommende Werkzeugkomponente an (Agentendateien in Versuch 2, MCP-Server in Versuch 3). Erst dieser angepasste Prompt bildet den Ausgangspunkt für den Iterations-Loop des Folgeversuchs. In Versuch 4 wird die Logik umgekehrt. Die Werkzeugkonfiguration ist die Konstante, das Modell die Variable. Damit lassen sich Unterschiede in Versuch 1 bis 3 ursächlich der Werkzeugvariation und in Versuch 4 ursächlich der Modellwahl zuordnen.
@@ -0,0 +1,72 @@
#set text(lang: "de")
#heading(level: 2)[Stakeholder-Validierung als Verifikationsverfahren]
Die Stakeholder-Validierung ist das zentrale Verifikationsverfahren dieser Arbeit. Sie ist nicht als nachgelagerter Schritt gedacht, sondern bildet das Maß, an dem die KI-Ergebnisse gemessen werden. Plausibel formulierte LLM-Ausgaben sind nicht hinreichend. Eine Anforderung gilt erst dann als belastbar, wenn sie durch einen Domänenexperten geprüft und bestätigt wurde.
Vorgesehen sind bis zu drei Validatoren mit jeweils mehrjähriger Erfahrung in der c-entron-Codebasis und in den fachlich abgedeckten Geschäftsprozessen. Da im Unternehmen nicht mehr für jeden Fachbereich ein dedizierter Experte verfügbar ist, kann eine vollständige modulweise Abdeckung durch bereichsspezifische Validatoren nicht garantiert werden. Die Validatorengruppe deckt die Codebasis stattdessen in Summe ab. Die Teilnehmer sind bereits identifiziert. Der Interview-Leitfaden ist in @anh_interview_validierung im Anhang dokumentiert.
Für die Validierung wird pro Versuchslauf eine Zufallsstichprobe aus den extrahierten Anforderungen gezogen und auf die bis zu drei Teilnehmer verteilt. Die Stichprobengröße wird vor Versuchsbeginn festgelegt und im Versuchsprotokoll dokumentiert. Eine bereichs- oder risikoklassenspezifische Stratifizierung ist nicht vorgesehen, da die personelle Verfügbarkeit der Fachexperten eine flächendeckende Modulabdeckung nicht zulässt.
Jede Anforderung wird entlang von sechs Dimensionen bewertet:
/ Sachliche Korrektheit: Beschreibt die Anforderung das tatsächliche Systemverhalten?
/ Vollständigkeit: Sind Akteur, Vorbedingung und Ergebnis ausreichend spezifiziert?
/ Verständlichkeit: Lässt sich die Anforderung ohne Rückfrage interpretieren?
/ Redundanzfreiheit: Ist die Anforderung von anderen klar abgegrenzt?
/ Übernahmewürdigkeit: Soll die Anforderung im Zielsystem in ihrer Funktion erhalten bleiben, oder ist sie ein historisch gewachsener Workaround, ein Sonderfall oder eine veraltete Logik, die im Zuge der Neuimplementierung entfallen sollte?
/ Konsolidierungsbedarf: Bestehen andere Anforderungen, die dieselbe fachliche Funktion abbilden und im Zielsystem zu einer gemeinsamen Anforderung zusammengeführt werden sollten? Ein typisches Beispiel sind die in der bestehenden c-entron-Codebasis getrennt geführten Datensätze für Drucker („Stammblätter") und sonstige Hardware („Assets"), die im Zielsystem zu einem konsolidierten Asset-Konzept zusammengeführt werden sollen.
Die Bewertung erfolgt auf einer fünfstufigen Likert-Skala mit definierten Ankern an den Polen, wobei 1 für „trifft nicht zu" und 5 für „trifft voll zu" steht.
Ergänzend zur itemweisen Bewertung werden mit den Validatoren halbstrukturierte Interviews geführt. Themen sind hierbei vor allem migrationsspezifische Risiken, der Konsolidierungsbedarf gegenüber der Zielarchitektur und die Nützlichkeit der KI-Ergebnisse im Vergleich zu einer hypothetischen manuellen Analyse.
Eine Requirement gilt im Sinne dieser Arbeit als belastbar, wenn drei Quellen sie stützen: ein KI generiertes Requirement, ein konkreter Artefakt-Beleg und eine Expertenbestätigung.
#heading(level: 2)[Evaluationsrahmen]
Der Evaluationsrahmen wird vor der Durchführung der Versuche definiert. Damit wird eine nachträgliche Anpassung der Kriterien an die Ergebnisse ausgeschlossen. Die Bewertung orientiert sich an den drei in @sec_re_qualitaet hergeleiteten Qualitätsdimensionen.
/ Statement-Qualität: Pro einzelner Anforderung wird gemessen, ob sie eindeutig formuliert, vollständig im Satzbau, frei von unbelegten Annahmen und mit Akzeptanzkriterium oder Prüfidee versehen ist. Die Messung erfolgt über die zuvor beschriebene Likert-Skala.
/ Set-Qualität: Pro Versuch wird die gesamte erzeugte Anforderungsmenge als ein Set bewertet. Gemessen wird, ob sie die relevanten Prozesse und Varianten vollständig abdeckt, in sich konsistent ist und keine Doubletten enthält. Im Vordergrund steht dabei die Vollständigkeit, weil ein fehlendes Requirement im Migrationskontext zu Funktionsverlust führen kann. Die Bewertung erfolgt qualitativ durch Expertenbewertung und ergänzend durch maschinelle Konsistenzprüfungen wie doppelte IDs oder fehlende Belege.
/ Traceability-Qualität: Pro Beleg-Verknüpfung wird gemessen, ob der Beleg reproduzierbar auffindbar ist, etwa über Dateipfad, Methode oder SQL-Query, und ob die Ableitung vom Beleg zur Anforderung nachvollziehbar bleibt.\ \
Ergänzend zur Qualitätsbewertung wird der Aufwand erhoben. Dazu werden während der KI-Läufe Indikatoren wie Tokenkosten un Bearbeitungsdauer protokolliert. Parallel wird grob abgeschätzt, wie viele Stunden ein erfahrener Analyst für dieselben Module ohne KI-Unterstützung gebraucht hätte. Beide Größen zusammen erlauben einen Vergleich der Größenordnung, nicht jedoch einen exakten Effizienzfaktor.
#heading(level: 2)[Reproduzierbarkeit und Risikomanagement]
Reproduzierbarkeit und Risikomanagement sind als querschnittliche Aspekte angelegt. Sie betreffen alle Versuchsdurchläufe gleichermaßen und werden hier zusammengefasst.
Alle steuerungsrelevanten Artefakte werden versioniert vorgehalten. Hierzu zählen die verwendeten Prompts in ihrer Textfassung inklusive aller Iterationsversionen mit Zeitstempel und Änderungsgrund, die Agentendateien mit ihren Rollenbeschreibungen, die MCP-Server-Konfigurationen sowie die Angaben zu Modellversion und Kontextfenstergröße. Für lokal betriebene Modelle werden zusätzlich die Inferenz-Runtime mit Version (LM Studio), die Quantisierungsstufe des verwendeten Modell-Builds sowie weitere Sampling-Parameter dokumentiert, da diese Stellgrößen die Reproduzierbarkeit der Ausgaben spürbar beeinflussen. Jeder Versuchsordner enthält die vollständige Konfiguration als Single Source. Wo möglich, werden deterministische Einstellungen gewählt.
Da die Codebasis kundenbezogene Strukturen enthält, werden datenschutzkritische Werkzeuge bewusst eingegrenzt. MCP-Server für Datenbank-Inspektion und Symbol-Navigation werden lokal betrieben. An externe LLM-Anbieter werden nur diejenigen Codeausschnitte gesendet, die für den jeweiligen Analyseschritt notwendig sind. Personenbezogene Daten oder vollständige Datenexporte sind ausgeschlossen.
Die folgenden vier Risikokategorien werden adressiert:
/ Halluzinationen: Begegnet durch Belegpflicht und Stakeholder-Validierung. Jede Anforderung ohne nachvollziehbaren Beleg wird als Hypothese markiert.
/ Reproduzierbarkeitsverlust: Begegnet durch versionierte Prompts und deterministische Einstellungen, soweit das Modell sie unterstützt. Da die innere Iteration zudem die Gefahr eines Autoren-Bias birgt, etwa eines unbewussten Hinsteuerns auf bestimmte Module, werden die Prompt-Änderungen pro Iteration mit Änderungsgrund dokumentiert und sind im Versuchsordner nachvollziehbar.
/ Domänen- und Datenbias: Begegnet durch eine Stichprobenwahl, die alle relevanten Module abdeckt und nicht nur die in der KI-Ausgabe häufig auftauchenden.
/ Datenschutzverletzungen: Begegnet durch On-Premise-MCP, kontrollierten Versand und Logging der externen Aufrufe.
#heading(level: 2)[Konkrete Konfigurationen der geplanten Versuche]
Dieser Abschnitt konkretisiert die zuvor beschriebene Versuchsreihe auf Konfigurationsebene. Jeder Versuch ist durch seinen Prompt, seine Agentenliste und seine MCP-Server-Liste vollständig beschrieben. Modellversion, Kontextfenster und Temperatur werden im Versuchsordner protokolliert.
#figure(
table(
columns: (auto, 1fr, 1fr, 1fr),
align: (left, left, left, left),
stroke: 0.4pt,
[*Element*], [*V1 Baseline*], [*V2 Agenten*], [*V3 MCP-Tools*],
[Modell], [Claude (Claude Code)], [Claude (Claude Code)], [Claude (Claude Code)],
[Startprompt], [Zu Versuchsbeginn neu formuliert], [Finaler Prompt aus V1, angepasst an Agentendateien], [Finaler Prompt aus V2, angepasst an MCP-Server],
[Agentendateien], [keine], [Stakeholder, System, Software, ISO-29148-Orchestrator], [wie V2],
[MCP-Server], [keine], [keine], [Symbol-Navigation, Datenbank-Inspektion, optional GUI-Beobachtung],
[Validierungsstichprobe], [Zufallsstichprobe], [Zufallsstichprobe], [Zufallsstichprobe],
),
caption: [Detail-Konfiguration der drei Kernversuche.],
) <tab_versuchskonfiguration>
Die Versuchsordner-Struktur folgt einer einheitlichen Konvention. Pro Versuch existiert ein Unterordner mit den Konfigurationsartefakten, ein Eingangsprotokoll mit Modell- und Werkzeugangaben, ein Ergebnis-Unterordner sowie eine Verlaufsdokumentation für die Validierungsschritte. Damit ist jeder Versuch eigenständig reproduzierbar.
@@ -0,0 +1,10 @@
#let __is_thesis = context { query(<__thesis_document>).len() > 0 }
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Durchführung und Ergebnisse]
// TODO Variante B – Inhalte aus 05_prototypische_umsetzung_VarianteA.typ übernehmen
// und explizit als „Ergebnisse der Versuche" strukturieren (keine Versuchs-Logbuch-Optik).
-177
View File
@@ -1,177 +0,0 @@
#let __is_thesis = context { query(<__thesis_document>).len() > 0 }
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Ergebnisse (ca. 10 Seiten)]
Dieses Kapitel dokumentiert die tatsaechlich erzeugten Ergebnisse der drei Versuche (V01-V03). Neben den Kennzahlen werden die Ergebnisartefakte aus den jeweiligen Ergebnisverzeichnissen strukturiert aufgelistet und durch exemplarische Requirements bzw. Use Cases belegt.
#heading(level: 2)[Ergebnisueberblick]
#table(
columns: (1fr, 1fr, 1fr, 1fr),
stroke: 0.4pt,
[**Kennzahl**], [**V01**], [**V02**], [**V03**],
[Konsolidierte Anforderungen/Faehigkeiten], [277], [220], [1720],
[Formale Anforderungen (StRS+SyRS+SwRS)], [277], [220], [0],
[Explizite Use Cases], [0], [46], [1720],
[Undokumentierte Use Cases], [n.v.], [n.v.], [1211],
[ISO-29148-Compliance], [qualitativ A+], [96,1%], [n.v.],
[Traceability], [100% laut Doku], [100% bidirektional], [n.v.],
[Ergebnisdateien gesamt], [11], [37], [30]
)
#heading(level: 2)[V01 Ergebnisse (Baseline)]
#heading(level: 3)[Ergebnisdateien in `Versuche/Versuch 01/Ergebnisse`]
- `Versuche/Versuch 01/Ergebnisse/Centron_Software_Requirements_Specification.md`
- `Versuche/Versuch 01/Ergebnisse/Centron_Software_Requirements_Specification.pdf`
- `Versuche/Versuch 01/Ergebnisse/complete-iso29148-requirements-specification.md`
- `Versuche/Versuch 01/Ergebnisse/ISO29148_Complete_Requirements_Specification.md`
- `Versuche/Versuch 01/Ergebnisse/iso29148-integrated-requirements-analysis.md`
- `Versuche/Versuch 01/Ergebnisse/iso29148-integrated-requirements-analysis.pdf`
- `Versuche/Versuch 01/Ergebnisse/nhibernate-orm-analysis.md`
- `Versuche/Versuch 01/Ergebnisse/software/SwRS_Complete_Detailed.md`
- `Versuche/Versuch 01/Ergebnisse/software/SwRS_Complete_Detailed.pdf`
- `Versuche/Versuch 01/Ergebnisse/system/SyRS_Complete_Detailed.md`
- `Versuche/Versuch 01/Ergebnisse/system/SyRS_Complete_Detailed.pdf`
#heading(level: 3)[Beispielhafte Requirements aus den Ergebnisdateien]
```text
StR-001: Comprehensive Customer Account Management
Statement: The system shall provide comprehensive customer account management capabilities...
(Quelle: ISO29148_Complete_Requirements_Specification.md)
```
```text
FR-001: User Authentication System
Requirement: The system shall provide secure user authentication...
(Quelle: system/SyRS_Complete_Detailed.md)
```
#heading(level: 2)[V02 Ergebnisse (ISO-Konsolidierung mit Agenten)]
#heading(level: 3)[Ergebnisdateien in `Versuche/Versuch 02/Ergenisse`]
- `Versuche/Versuch 02/Ergenisse/COMPLETE_REQUIREMENTS_SPECIFICATION.md`
- `Versuche/Versuch 02/Ergenisse/COMPLETE_REQUIREMENTS_SPECIFICATION.pdf`
- `Versuche/Versuch 02/Ergenisse/README.md`
- `Versuche/Versuch 02/Ergenisse/TABLE_FORMATTING_STATUS.md`
- `Versuche/Versuch 02/Ergenisse/.execution_state/baseline_metrics.json`
- `Versuche/Versuch 02/Ergenisse/.execution_state/directory_setup.txt`
- `Versuche/Versuch 02/Ergenisse/.execution_state/milestone_state.json`
- `Versuche/Versuch 02/Ergenisse/.execution_state/project_structure.json`
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Executive_Summary.md`
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Master_Requirements.md`
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Quality_Report.md`
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Traceability_Master.csv`
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Validation_Checklist.md`
- `Versuche/Versuch 02/Ergenisse/software/Analysis_Complete.md`
- `Versuche/Versuch 02/Ergenisse/software/Business_Rules.md`
- `Versuche/Versuch 02/Ergenisse/software/Integration_Patterns.md`
- `Versuche/Versuch 02/Ergenisse/software/Pattern_Catalog.csv`
- `Versuche/Versuch 02/Ergenisse/software/Performance_Patterns.md`
- `Versuche/Versuch 02/Ergenisse/software/Security_Patterns.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_Algorithms.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_CodeCatalog.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_Complete.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_DataModel.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_TestSpecification.md`
- `Versuche/Versuch 02/Ergenisse/software/SwRS_Traceability.csv`
- `Versuche/Versuch 02/Ergenisse/software/Validation_Rules.md`
- `Versuche/Versuch 02/Ergenisse/stakeholder/StRS_Complete.md`
- `Versuche/Versuch 02/Ergenisse/stakeholder/StRS_Diagrams.md`
- `Versuche/Versuch 02/Ergenisse/stakeholder/StRS_Evidence.md`
- `Versuche/Versuch 02/Ergenisse/stakeholder/StRS_Summary.md`
- `Versuche/Versuch 02/Ergenisse/stakeholder/StRS_Traceability.csv`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_API_Specification.yaml`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Architecture.md`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Complete.md`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Interfaces.md`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Summary.md`
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Traceability.csv`
#heading(level: 3)[Beispielhafte Requirements aus den Ergebnisdateien]
```text
SyR-001: The system SHALL implement a multi-layered architecture with clear separation of concerns.
(Quelle: Ergenisse/system/SyRS_Complete.md)
```
```text
SyR-013: The system SHALL provide secure user authentication with multi-factor authentication support.
(Quelle: Ergenisse/system/SyRS_Complete.md)
```
```text
SW-ARCH-001: The software SHALL implement a 6-layer architecture pattern.
(Quelle: Ergenisse/software/SwRS_Complete.md)
```
#heading(level: 2)[V03 Ergebnisse (Discovery-Erweiterung mit Agenten und MCP)]
#heading(level: 3)[Ergebnisdateien in `Versuche/Versuch 03/ERP_DOCUMENTATION`]
- `Versuche/Versuch 03/ERP_DOCUMENTATION/ANALYSIS_SUMMARY.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/BUSINESS_GLOSSAR_MIT_DB_MAPPING.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/BUSINESS_GLOSSAR.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/BUSINESS_GLOSSAR.pdf`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/COMPLETE_DATABASE_SCHEMA.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/DOCUMENTATION_INDEX.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/EXPORT_COMPLETE_SCHEMA.sql`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/README_USE_CASE_ANALYSIS.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/README.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/SCREENSHOT_ANALYSIS_SUMMARY.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/SCREENSHOT_MAPPING_COMPLETE.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/SCREENSHOT_PROJECT_INDEX.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/SSMS_DB_SCHEMA.sql`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_DATABASE_MODELS.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_REST_API.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_SUMMARY.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_WORKFLOWS.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASE_ANALYSIS_README.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASE_MAPPING.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS_DE.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS_DE.pdf`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS.pdf`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW_CONTROLLERS.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW_GUI_MAPPING.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW_IMPLEMENTATION_GUIDE.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW_XAML_TEMPLATES.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_NEW.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES.md`
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES.pdf`
#heading(level: 3)[Beispielhafte Use Cases aus den Ergebnisdateien]
```text
1.1.1 Personalized User Welcome
Purpose: Display personalized greeting with user name and context-aware dashboard content.
(Quelle: ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS.md)
```
```text
1.1.6 Work Status Alerts
Purpose: Alert users to missing or incomplete work time entries.
(Quelle: ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS.md)
```
```text
Key Finding: 1,720+ use cases discovered; current documentation gap: 71%.
(Quelle: ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_SUMMARY.md)
```
#heading(level: 2)[Ergebnisfazit]
Die Ergebnislage zeigt drei komplementaere Stufen:
- **V01** liefert eine belastbare formale Baseline.
- **V02** liefert die staerkste ISO-29148-konforme Konsolidierung mit hoher Traceability.
- **V03** liefert die groesste funktionale Breite und identifiziert den groessten Dokumentations-Gap.
Damit liegt eine vollstaendige empirische Grundlage fuer die anschliessende Evaluation (Kapitel 6) vor: formal-strukturierte Requirements (V01/V02) plus breite Discovery-Evidenz (V03).
+3 -117
View File
@@ -4,121 +4,7 @@
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Evaluation (ca. 12 Seiten)]
#heading(level: 1)[Evaluation]
Die Evaluation folgt der in Kapitel 4 beschriebenen Iterationslogik und bewertet die drei real durchgefuehrten Versuche (V01-V03) vergleichend. Ziel ist nicht die Darstellung eines einzelnen "besten" Laufs, sondern die Einordnung der methodischen Entwicklung von einer Baseline ueber eine formale ISO-Konsolidierung bis zur anschliessenden Discovery-Erweiterung.
#heading(level: 2)[Evaluationsdesign und Datenbasis]
Die Auswertung basiert ausschliesslich auf den erzeugten Artefakten in:
- `Versuche/Versuch 01/Versuch01.md` und `Versuche/Versuch 01/Requirements.md`,
- `Versuche/Versuch 02/Versuch02.md` und `Versuche/Versuch 02/Requirements.md`,
- `Versuche/Versuch 03/Versuch03.md` und `Versuche/Versuch 03/Requirements.md`.
Dabei wurden nur konsolidierte, in den Dateien ausgewiesene Kennzahlen uebernommen. Fokus der Bewertung:
1. Umfang der rekonstruierten Faehigkeiten/Requirements,
2. Formalisierungsgrad (StRS/SyRS/SwRS vs. reine Use-Case-Discovery),
3. Traceability- und ISO-29148-Naehe,
4. methodischer Nutzen der eingesetzten Tooling-Konfiguration.
#heading(level: 2)[Quantitative Ergebnisse der Versuchsreihe]
#table(
columns: (1fr, 1fr, 1fr, 1fr),
stroke: 0.4pt,
[**Kennzahl**], [**V01**], [**V02**], [**V03**],
[Konsolidierte Requirements/Faehigkeiten], [277], [220], [1720],
[Formale Requirements (StRS+SyRS+SwRS)], [277], [220], [0],
[StRS / SyRS / SwRS], [35 / 75 / 167], [84 / 53 / 83], [0 / 0 / 0],
[Explizite Use Cases], [0], [46], [1720 (Use-Case-fokussiert)],
[Undokumentierte Use Cases], [n.v.], [n.v.], [1211],
[ISO-29148-Compliance], [qualitativ A+], [96,1% (100% mandatory)], [n.v.],
[Traceability], [100% laut Doku], [100% bidirektional], [n.v.],
[Ergebnisdateien gesamt], [11], [37], [30]
)
Ergaenzende Kontextkennzahlen aus den Versuchsdateien:
- V01: Analyse von 34 C\#-Projekten und 12.507+ Source Files.
- V02: 14.940 Dateien (13.717 C\#, 1.189 XAML, 34 Projekte), 46 explizite Use Cases in die formale Requirements-Struktur integriert.
- V03: 150.000+ LoC analysiert, 3.412 potenzielle Use Cases identifiziert, 71% dokumentationsbezogener Gap (1211 von 1720 Use Cases vormals undokumentiert).
#heading(level: 2)[Vergleichende Analyse]
#heading(level: 3)[Versuch 01: Formale Baseline ohne Tooling-Erweiterung]
V01 zeigt, dass bereits ohne Agenten/MCP eine formal strukturierte Requirements-Spezifikation erzeugt werden kann. Die Staerke liegt in der klaren Dreiebenenstruktur (StRS/SyRS/SwRS). Die Schwaeche ist die begrenzte Discovery-Perspektive: explizite Use-Case-Rekonstruktion und Gap-Bewertung bleiben gering ausgepraegt.
#heading(level: 4)[Prompt, Agenten und Ergebnisbeispiele (V01)]
- **Verwendeter Prompt:** "Please analyze this software project and write a reuqirements specification according to modern standards."
- **Agentenbeispiele:** Keine Agenten (bewusste Baseline ohne agentische Zerlegung und ohne MCP).
- **Beispielhafte Ergebnis-Requirements:**
- `Versuche/Versuch 01/Ergebnisse/ISO29148_Complete_Requirements_Specification.md`: u. a. `StR-001` (Comprehensive Customer Account Management).
- `Versuche/Versuch 01/Ergebnisse/system/SyRS_Complete_Detailed.md`: u. a. `FR-001` (User Authentication System) und `FR-002` (Role-Based Access Control).
- `Versuche/Versuch 01/Ergebnisse/software/SwRS_Complete_Detailed.md`: softwareseitige Architektur- und Umsetzungsanforderungen im SwRS-Format.
#heading(level: 3)[Versuch 02: ISO-orientierte Konsolidierung mit Agenten]
V02 fokussiert die formale Konsolidierung und liefert eine ISO-29148-nahe Zielstruktur mit hoher Traceability. Mit 220 konsolidierten Requirements, 96,1% ISO-29148-Compliance und 100% bidirektionaler Traceability ist der Lauf methodisch sauber und reviewfaehig. Gleichzeitig zeigte sich die zentrale Grenze dieses Schritts: Die reine ISO-orientierte Ableitung war fuer den Gesamtumfang zu rigide und fuer die Discovery-Breite nicht vollumfaenglich genug.
#heading(level: 4)[Prompt, Agenten und Ergebnisbeispiele (V02)]
- **Verwendeter Prompt:** "Please analyze this software project and write a ISO 29148 compliant reuqirements specification. Use Agents wherever possible."
- **Agentenbeispiele:**
- `Versuche/Versuch 02/Tools/agents/iso29148-master-orchestrator-agent.md`
- `Versuche/Versuch 02/Tools/agents/iso29148-stakeholder-agent.md`
- `Versuche/Versuch 02/Tools/agents/iso29148-system-requirements-agent.md`
- `Versuche/Versuch 02/Tools/agents/iso29148-software-requirements-agent`
- **Beispielhafte Ergebnis-Requirements:**
- `Versuche/Versuch 02/Ergenisse/system/SyRS_Complete.md`: u. a. `SyR-001` (Multi-Layer Architecture), `SyR-002` (Dual Data Access Pattern), `SyR-013` (Authentication).
- `Versuche/Versuch 02/Ergenisse/software/SwRS_Complete.md`: u. a. `SW-ARCH-001` (6-Layer Architecture), `SW-ARCH-002` (ILogic-Pattern), `SW-FUNC-001` (Account Management).
- `Versuche/Versuch 02/Ergenisse/master/ISO29148_Quality_Report.md`: qualitaetssichernde Gesamtbewertung (u. a. 100% Traceability).
#heading(level: 3)[Versuch 03: Discovery-Erweiterung mit Agenten und MCP]
V03 erweitert deshalb die Methodik um MCP-gestuetzte Discovery. Der Lauf vergroessert die funktionale Breite deutlich (1720 konsolidierte Faehigkeiten, davon 1211 vormals undokumentierte Use Cases) und eignet sich besonders fuer Gap-Analysen und Vollstaendigkeitspruefung. Die Kehrseite ist ein geringerer Formalisierungsgrad gegenueber der ISO-Konsolidierung.
#heading(level: 4)[Prompt, Agenten und Ergebnisbeispiele (V03)]
- **Verwendeter Prompt:** "Please analyze this software project and write a reuqirements specification according to modern standards. Use Agents and MCP servers wherever possible. Keep superflous texts to a minimum and concentrate on actual requirements."
- **Agentenbeispiele:**
- `Versuche/Versuch 03/Tools/Agents/centron-documentation-writer.md`
- `Versuche/Versuch 03/Tools/Agents/nhibernate-query-reviewer.md`
- `Versuche/Versuch 03/Tools/Agents/centron-code-reviewer.md`
- `Versuche/Versuch 03/Tools/Agents/webservice-developer.md`
- **MCP-Beispiele:** Serena-MCP (Memory), Windows-MCP (UI-Interaktion), MSSQL-MCP (DB-Schemazugriff).
- **Beispielhafte extrahierte Use-Case-/Anforderungsartefakte:**
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES_CENTRON_NEXUS.md`: u. a. Use Cases `1.1.1` (Personalized User Welcome), `1.1.6` (Work Status Alerts), `3.1` (Quick Ticket Creation).
- `Versuche/Versuch 03/ERP_DOCUMENTATION/USE_CASES.md`: moduluebergreifende, strukturierte Use-Case-Dokumentation fuer c-entron.NET.
- `Versuche/Versuch 03/ERP_DOCUMENTATION/UNDOCUMENTED_USE_CASES_SUMMARY.md`: 1.720+ Use Cases und ca. 71% Dokumentations-Gap als Discovery-Nachweis.
#heading(level: 2)[Abgleich mit den geplanten Methoden]
Der Soll-Ist-Abgleich zeigt eine hohe Passung zur geplanten Gesamtmethodik, wenn diese als iterative Kombination aus *Discovery* und *Konsolidierung* verstanden wird:
- Die Standardrecherche (ISO/IEC/IEEE 29148) wurde fruehzeitig umgesetzt.
- Ein Baseline-Lauf ohne Spezialisierung wurde durchgefuehrt (V01).
- Eine strukturierte ISO-Konsolidierung wurde realisiert (V02).
- Danach wurde die Abdeckung durch MCP-gestuetzte Discovery erweitert (V03), weil der ISO-Lauf allein zu rigide und nicht vollumfaenglich genug war.
Abweichung zur urspruenglich linearen Planung: Stakeholder-Interviews und flaechendeckende fachliche Reviews wurden in der betrachteten Phase noch nicht vollstaendig abgeschlossen. Die Methodik wird deshalb in der Ergebnisinterpretation als "technisch validierte Vorstufe" einer finalen fachlichen Konsolidierung eingeordnet.
#heading(level: 2)[Bewertung der Forschungsleitfragen auf Basis der aktuellen Evidenz]
- **F1 (reproduzierbarer LLM-Einsatz):** beantwortbar. Die drei Versuche zeigen, dass reproduzierbare Prozessschritte und klar unterscheidbare Konfigurationen moeglich sind.
- **F2 (Ableitung aus Code vs. Zusatzquellen):** teilweise beantwortbar. Codebasierte Extraktion funktioniert, video- und interviewbasierte Ergaenzungen sind noch offen.
- **F3 (Qualitaet aus Expertensicht):** noch nicht abschliessend beantwortbar, da systematische Expertenratings nicht vollstaendig dokumentiert vorliegen.
- **F4 (Chancen und Grenzen):** beantwortbar. Chancen liegen in Skalierung und Strukturierung; Grenzen in Halluzinationsrisiken, fehlender Vollstaendigkeit ohne Zusatzquellen und hohem Konsolidierungsbedarf.
#heading(level: 2)[Limitationen]
Die aktuelle Evidenz ist durch drei Punkte begrenzt:
1. Vollstaendige Video-Transkription und -Auswertung fehlen noch.
2. Ein methodischer Endabgleich zwischen Video- und Codeperspektive ist noch nicht abgeschlossen.
3. Die fachliche Endklassifikation aller Use-Case-Cluster (Ja/Nein/Neu/TBD) liegt noch nicht durchgaengig vor.
Diese Limitationen betreffen vor allem die finale Vollstaendigkeitsaussage, nicht jedoch die grundlegende Wirksamkeit der iterativen Methodik.
// TODO Variante B – Anwendung des in Kap 4.6 definierten Evaluationsrahmens auf die
// Ergebnisse aus Kap 5. Keine Erst-Definition von Kriterien hier.
+1 -33
View File
@@ -4,46 +4,14 @@
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Diskussion (ca. 8 Seiten)]
#heading(level: 1)[Diskussion ]
#heading(level: 2)[Interpretation der Ergebnisse]
Die Ergebnisse zeigen einen klaren methodischen Lerneffekt ueber die drei Iterationen. Der Verlauf von V01 ueber V02 zu V03 ist nicht als Widerspruch, sondern als komplementaere Reifung zu interpretieren:
- V01 demonstriert, dass bereits mit einfacher Konfiguration formal strukturierte Requirements ableitbar sind.
- V02 zeigt, dass eine agentengestuetzte ISO-Konsolidierung methodisch sauber, aber fuer den Gesamtumfang zu rigide sein kann.
- V03 zeigt, dass die MCP-Erweiterung die funktionale Breite massiv erhoeht und Discovery-Luecken schliesst.
In Summe entsteht ein zweistufiges Zielbild fuer Reverse Requirements Engineering in Legacy-Projekten: zuerst *formal konsolidieren*, danach *gezielt in die Breite erweitern*.
#heading(level: 2)[Chancen und Grenzen]
Die wesentlichen Chancen des Ansatzes liegen in:
- hoher Skalierbarkeit bei grossen Legacy-Artefakten,
- schneller Sichtbarmachung undokumentierter Funktionalitaet,
- strukturierter Ueberfuehrung in reviewbare Requirements-Artefakte.
Die zentralen Grenzen bleiben:
- keine belastbare Vollstaendigkeit ohne Zusatzquellen (insbesondere Nutzungs- und Prozesssicht),
- Halluzinations- und Fehlinterpretationsrisiken ohne Beleg- und Reviewpflicht,
- hoher Konsolidierungsaufwand zwischen Discovery-Artefakten und abnahmefaehiger Spezifikation.
Damit bestaetigt die Fallstudie, dass LLMs Requirements Engineering nicht ersetzen, aber als beschleunigendes Analyseinstrument mit klaren Governance-Regeln substantiellen Mehrwert liefern.
#heading(level: 2)[Implikationen fuer Forschung und Praxis]
Fuer die Praxis folgt daraus ein umsetzbarer Einfuehrungspfad:
1. Iterative Versuchslogik statt einmaliger "Big-Bang"-Extraktion.
2. Trennung von Discovery- und Konsolidierungsphase als Standard.
3. Traceability als verpflichtendes Abnahmekriterium fuer LLM-Ergebnisse.
Fuer die Forschung ergeben sich drei Anschlussfragen:
1. Wie laesst sich die Triangulation aus Code-, Video- und Stakeholderdaten automatisiert zusammenfuehren?
2. Welche Metriken messen Qualitaet von Requirements-Artefakten robuster als reine Umfangszahlen?
3. Wie kann Human-in-the-loop-Validierung mit vertretbarem Aufwand skaliert werden?
Die vorliegende Arbeit liefert dafuer eine belastbare methodische Ausgangsbasis, zeigt aber zugleich, dass die letzte Meile zur fachlich finalen Spezifikation weiterhin ein kooperativer Mensch-KI-Prozess bleibt.
+1 -34
View File
@@ -1,35 +1,2 @@
#heading(level: 1)[Fazit und Ausblick (ca. 4 Seiten)]
#heading(level: 1)[Fazit und Ausblick]
#heading(level: 2)[Zusammenfassung und Beantwortung der Forschungsfragen]
Die Arbeit zeigt, dass KI-gestuetztes Reverse Requirements Engineering im untersuchten Legacy-ERP-Kontext praktikabel ist, wenn der Prozess iterativ und kontrolliert aufgebaut wird. Die drei durchgefuehrten Versuche liefern dabei komplementaere Staerken:
- V01 liefert eine formale Baseline mit klarer Requirements-Struktur.
- V02 konsolidiert die Erkenntnisse in eine ISO-29148-nahe, traceability-starke Spezifikation.
- V03 erweitert die Discovery-Breite per MCP und deckt einen hohen dokumentationsbezogenen Gap auf.
Damit ist F1 (prozessuale Einsetzbarkeit von LLMs) positiv beantwortet. F4 (Chancen und Grenzen) ist ebenfalls klar beantwortbar: Hohe Effizienz- und Strukturgewinne stehen einem weiterhin relevanten Validierungs- und Konsolidierungsbedarf gegenueber. F2 und F3 sind teilweise beantwortet, da video- und interviewbasierte Endvalidierung noch nicht vollstaendig abgeschlossen ist.
#heading(level: 2)[Handlungsempfehlungen fuer c-entron GmbH]
Aus den Ergebnissen lassen sich folgende priorisierte Handlungsschritte ableiten:
1. **V02 als Spezifikationsbasis verwenden:** Die 220 konsolidierten Requirements mit hoher Traceability als Arbeitsgrundlage fuer die Web-Migration etablieren.
2. **V03 als Discovery-Backlog nutzen:** Die 1720 identifizierten Faehigkeiten systematisch gegen V02 mappen, um potenzielle Luecken sichtbar zu halten.
3. **Review-Governance fest verankern:** Fachliche Freigaben und Aenderungsentscheidungen pro Requirement dokumentieren (kein unreviewter LLM-Output im Zielbacklog).
4. **Toolchain standardisieren:** Prompt-/Agentenkonfigurationen versionieren, damit Folgeanalysen reproduzierbar bleiben.
#heading(level: 2)[Ausblick und naechste Schritte]
Die naechste Arbeitsphase erweitert die bisher codezentrierte Evidenz um die noch offenen Schritte aus dem Protokoll:
1. **Vollstaendige Videoanalyse:** Alle vorhandenen Schulungsvideos KI-gestuetzt transkribieren und strukturiert auf Use Cases auswerten.
2. **Abgleich Video vs. Codeanalyse:** Systematischer Vergleich, ob und wo sich beide Sichten decken bzw. welche Use Cases nur in einer Quelle auftauchen.
3. **Clusterung in abstrakte Konzepte:** Die identifizierten Use Cases in die bereits vorbereiteten 101 abstrahierten Konzepte ueberfuehren (vgl. `A_Videoanalyse_Uebersicht.csv`).
4. **Manuelle Fachklassifikation pro Cluster:** Bewertung in die Kategorien
- **ja:** unveraenderte Uebernahme,
- **nein:** Entfall in ERP Web,
- **neu:** fachlich vorhanden, aber neu zu konzipieren,
- **TBD:** vorlaeufig offen.
Erst mit dieser finalen Triangulation aus Code, Video und Fachbewertung ist eine belastbare Vollstaendigkeitsaussage fuer die Migrationsplanung moeglich.
+1 -1
View File
@@ -1,3 +1,3 @@
#heading(level: 1)[Literaturverzeichnis (ca. 3 Seiten)]
#heading(level: 1)[Literaturverzeichnis]
#bibliography("../literatur.bib", style: "apa")
+4 -2
View File
@@ -1,7 +1,9 @@
#heading(level: 1)[Anhang (ca. 6 Seiten)]
#heading(level: 1)[Anhang]
#heading(level: 2)[Interviewleitfäden]
#heading(level: 3)[Stakeholder-Validierung der KI-extrahierten Anforderungen] <anh_interview_validierung>
#heading(level: 2)[Zusätzliches Datenmaterial]
#heading(level: 2)[Konfigurationsdetails des Prototyps]
#heading(level: 2)[Konfigurationsdetails]
@@ -0,0 +1,18 @@
#let __is_thesis = context { query(<__thesis_document>).len() > 0 }
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Konzeption und methodisches Vorgehen (ca. 12 Seiten)]
// TODO Variante B – wird abschnittsweise gefüllt:
// 4.1 Forschungsdesign-Überblick
// 4.2 Bezugsrahmen: Der RRE-Prozess als Untersuchungsgegenstand
// 4.3 Werkzeugbasis: LLM-Auswahl und Claude Code
// 4.4 Untersuchungsdesign: Tooling-Ablation als kontrollierte Variation
// 4.5 Stakeholder-Validierung als zentrales Verifikationsverfahren
// 4.6 Evaluationsrahmen
// 4.7 Reproduzierbarkeit und Risikomanagement
// 4.8 Konkrete Konfigurationen der drei Versuche
// 4.9 Überleitung
@@ -0,0 +1,10 @@
#let __is_thesis = context { query(<__thesis_document>).len() > 0 }
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Ergebnisse (ca. 10 Seiten)]
// TODO Variante B – Inhalte aus 05_prototypische_umsetzung_VarianteA.typ übernehmen
// und explizit als „Ergebnisse der Versuche" strukturieren (keine Versuchs-Logbuch-Optik).
@@ -0,0 +1,10 @@
#let __is_thesis = context { query(<__thesis_document>).len() > 0 }
#if __is_thesis == false [
#set cite(style: "apa")
#hide(bibliography("../literatur.bib", style: "apa"))
]
#heading(level: 1)[Evaluation (ca. 12 Seiten)]
// TODO Variante B – Anwendung des in Kap 4.6 definierten Evaluationsrahmens auf die
// Ergebnisse aus Kap 5. Keine Erst-Definition von Kriterien hier.
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -1,10 +1,12 @@
#import "masterarbeit_style.typ": thesis
#set text(lang: "de")
#let meta = (thesis.meta)(
"KI-gestütztes Reverse Requirements Engineering bei Legacy-Software",
"Masterarbeit an der Hochschule Neu-Ulm",
"Christoph Schwörer",
"Master of Science",
"Master of Business Administration",
"Prof. Dr. Daniel Schallmo",
"XX 2026"
)
@@ -13,6 +15,10 @@
#pagebreak()
#(thesis.confidentiality)(meta)
#pagebreak()
#(thesis.declaration)(meta)
#pagebreak()
@@ -36,13 +42,16 @@
#outline(depth: 2, title: "Inhaltsverzeichnis")
#pagebreak()
#include "Kapitel/00_abstract.typ"
#include "Kapitel/01_einleitung.typ"
#include "Kapitel/02_theoretischer_hintergrund.typ"
#include "Kapitel/03_fallstudie.typ"
#pagebreak()
#include "Kapitel/04_konzeption_methodisches_vorgehen.typ"
#include "Kapitel/05_prototypische_umsetzung.typ"
#pagebreak()
#include "Kapitel/05_durchfuehrung_und_ergebnisse.typ"
#pagebreak()
#include "Kapitel/06_evaluation.typ"
#include "Kapitel/07_diskussion.typ"
#include "Kapitel/08_fazit_ausblick.typ"
+14
View File
@@ -0,0 +1,14 @@
**/.git
**/.idea
**/app.config
**/bin
**/obj
./.editorconfig
./azure
./deployment
./docker
./docs
./scripts
./src/centron/
./tests
bin
+64
View File
@@ -0,0 +1,64 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# and XAML files
[*.{cs,xaml}]
charset = utf-8-bom
# C# files
[*.cs]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_or_internal_field_should_be_camel_case_with___prefix.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_camel_case_with___prefix.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_camel_case_with___prefix.style = camel_case_with___prefix
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.camel_case_with___prefix.required_prefix = _
dotnet_naming_style.camel_case_with___prefix.required_suffix =
dotnet_naming_style.camel_case_with___prefix.word_separator =
dotnet_naming_style.camel_case_with___prefix.capitalization = camel_case
+63
View File
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto eol=lf
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
@@ -0,0 +1,149 @@
name: Sign artifacts
description: >
Signs files via azure/artifact-signing-action with up to three attempts.
The Microsoft timestamp server (timestamp.acs.microsoft.com) fails
intermittently; re-signing already-signed files is safe because signtool
replaces existing signatures, so failed batches can simply be retried.
inputs:
azure-tenant-id:
description: Azure tenant id used for authentication.
required: true
azure-client-id:
description: Azure client id used for authentication.
required: true
azure-client-secret:
description: Azure client secret used for authentication.
required: true
files:
description: Newline-separated list of files to sign.
required: false
default: ''
files-folder:
description: Folder containing the files to sign.
required: false
default: ''
files-folder-filter:
description: Comma-separated file extensions to sign within files-folder.
required: false
default: ''
files-folder-recurse:
description: Whether to search files-folder recursively.
required: false
default: 'false'
endpoint:
description: Artifact Signing endpoint.
required: false
default: https://weu.codesigning.azure.net/
signing-account-name:
description: Artifact Signing account name.
required: false
default: CentronCodesigning
certificate-profile-name:
description: Certificate profile name.
required: false
default: centroncert
runs:
using: composite
steps:
- name: Sign (attempt 1)
id: attempt1
continue-on-error: true
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
with:
azure-tenant-id: ${{ inputs.azure-tenant-id }}
azure-client-id: ${{ inputs.azure-client-id }}
azure-client-secret: ${{ inputs.azure-client-secret }}
endpoint: ${{ inputs.endpoint }}
signing-account-name: ${{ inputs.signing-account-name }}
certificate-profile-name: ${{ inputs.certificate-profile-name }}
files: ${{ inputs.files }}
files-folder: ${{ inputs.files-folder }}
files-folder-filter: ${{ inputs.files-folder-filter }}
files-folder-recurse: ${{ inputs.files-folder-recurse }}
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
exclude-environment-credential: false
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Wait before retry (attempt 2)
if: steps.attempt1.outcome == 'failure'
shell: pwsh
run: |
Write-Host 'Signing failed, retrying in 30 seconds...'
Start-Sleep -Seconds 30
- name: Sign (attempt 2)
id: attempt2
if: steps.attempt1.outcome == 'failure'
continue-on-error: true
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
with:
azure-tenant-id: ${{ inputs.azure-tenant-id }}
azure-client-id: ${{ inputs.azure-client-id }}
azure-client-secret: ${{ inputs.azure-client-secret }}
endpoint: ${{ inputs.endpoint }}
signing-account-name: ${{ inputs.signing-account-name }}
certificate-profile-name: ${{ inputs.certificate-profile-name }}
files: ${{ inputs.files }}
files-folder: ${{ inputs.files-folder }}
files-folder-filter: ${{ inputs.files-folder-filter }}
files-folder-recurse: ${{ inputs.files-folder-recurse }}
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
exclude-environment-credential: false
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Wait before retry (attempt 3)
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure'
shell: pwsh
run: |
Write-Host 'Signing failed again, retrying in 90 seconds...'
Start-Sleep -Seconds 90
- name: Sign (attempt 3)
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure'
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
with:
azure-tenant-id: ${{ inputs.azure-tenant-id }}
azure-client-id: ${{ inputs.azure-client-id }}
azure-client-secret: ${{ inputs.azure-client-secret }}
endpoint: ${{ inputs.endpoint }}
signing-account-name: ${{ inputs.signing-account-name }}
certificate-profile-name: ${{ inputs.certificate-profile-name }}
files: ${{ inputs.files }}
files-folder: ${{ inputs.files-folder }}
files-folder-filter: ${{ inputs.files-folder-filter }}
files-folder-recurse: ${{ inputs.files-folder-recurse }}
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
exclude-environment-credential: false
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
+431
View File
@@ -0,0 +1,431 @@
name: Build, sign, and publish
on:
workflow_dispatch:
pull_request:
branches:
- main
- 'release/**'
types:
- opened
- synchronize
- reopened
- ready_for_review
push:
branches:
- main
- 'release/**'
permissions:
contents: read
# Supersede in-flight runs of this workflow for the same pull request. Pushes to main and
# release branches are excluded so every commit there still gets a full, recorded result.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
name: Build and sign
if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) }}
runs-on:
group: self-hosted
labels: [self-hosted, Windows, X64]
timeout-minutes: 180
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
main_version: ${{ steps.version.outputs.main_version }}
env:
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
CENTRON_BUILD_RUNNING_IN_AZURE_PIPELINE: true
CENTRON_BUILD_IS_DEV_BUILD: false
steps:
- name: Show runner information
shell: pwsh
run: |
Write-Host "Runner: $env:RUNNER_NAME"
Write-Host "Computer: $env:COMPUTERNAME"
Write-Host "PowerShell: $($PSVersionTable.PSVersion)"
- name: Check out repository
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Normalize Git repository format
shell: pwsh
run: |
$workspace = [IO.Path]::GetFullPath($env:GITHUB_WORKSPACE)
$safeDirectories = @(git config --global --get-all safe.directory)
$safeDirectoriesExitCode = $LASTEXITCODE
if ($safeDirectoriesExitCode -notin 0, 1) {
throw 'Could not determine the configured Git safe directories.'
}
if ($safeDirectories -notcontains $workspace) {
git config --global --add safe.directory $workspace
if ($LASTEXITCODE -ne 0) {
throw "Could not register the GitHub workspace as a safe directory: $workspace"
}
}
$repositoryFormatVersion = git config --local --get core.repositoryFormatVersion
if ($LASTEXITCODE -ne 0) {
throw 'Could not determine the Git repository format version.'
}
if ($repositoryFormatVersion -eq '1') {
$extensions = @(git config --local --name-only --get-regexp '^extensions\.')
$extensionsExitCode = $LASTEXITCODE
if ($extensionsExitCode -notin 0, 1) {
throw 'Could not determine the active Git repository extensions.'
}
if ($extensions.Count -gt 0) {
throw "Cannot normalize the Git repository while extensions are active: $($extensions -join ', ')"
}
git config --local core.repositoryFormatVersion 0
if ($LASTEXITCODE -ne 0) {
throw 'Could not normalize the Git repository format for Nerdbank.GitVersioning.'
}
}
- name: Verify build environment
shell: pwsh
run: |
$sdkVersion = dotnet --version
if ($LASTEXITCODE -ne 0) {
throw "The .NET SDK could not be resolved using global.json."
}
if ($sdkVersion -notmatch '^10\.0\.') {
throw "Expected a .NET 10 SDK, but resolved '$sdkVersion'."
}
if ((git rev-parse --is-shallow-repository) -ne 'false') {
throw 'Repository was checked out without its full history.'
}
Write-Host "Resolved .NET SDK: $sdkVersion"
dotnet --info
- name: Set version and create NuGet packages
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- create-nuget-packages
- name: Determine build version
id: version
shell: pwsh
run: |
$versionText = [string](
& ".\dotnettools\nbgv.exe" get-version -v Version
)
if ($LASTEXITCODE -ne 0) {
throw "Nerdbank.GitVersioning failed with exit code $LASTEXITCODE."
}
$version = [Version]$versionText
$buildVersion = $version.ToString()
$mainVersion = $version.ToString(3)
Write-Host "Build version: $buildVersion"
Write-Host "Main version: $mainVersion"
"version=$buildVersion" >> $env:GITHUB_OUTPUT
"main_version=$mainVersion" >> $env:GITHUB_OUTPUT
$metadataDirectory = Join-Path $env:RUNNER_TEMP 'centron-build-metadata'
New-Item -Path $metadataDirectory -ItemType Directory -Force | Out-Null
Set-Content `
-LiteralPath (Join-Path $metadataDirectory 'version.txt') `
-Value $buildVersion `
-Encoding utf8NoBOM
- name: Prepare Artifact Signing dependencies
shell: powershell
run: |
if ((Get-PackageProvider -Name NuGet -ErrorAction Ignore) -eq $null) {
Install-PackageProvider -Name NuGet -Force -Scope CurrentUser
}
if ((Get-InstalledModule -Name PowerShellGet -MinimumVersion 2.2.1 -ErrorAction Ignore) -eq $null) {
Install-Module -Name PowerShellGet -MinimumVersion 2.2.1 -Scope CurrentUser -Force -AllowClobber
}
- name: Build Web Service
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- build-web-service-only
- name: Sign Web Service
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files: |
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Host.WindowsService.exe
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Host.WindowsService.dll
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\ConnectionManager\c-entron Connection Manager.exe
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\ConnectionManager\c-entron Connection Manager.dll
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Interfaces.dll
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.WebServices.Core.dll
${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Core.dll
- name: Create Web Service installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- set-dependencies-web-service build-web-service-installer-only
- name: Sign Web Service installer
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files-folder: ${{ github.workspace }}\deployment\centron\WebServiceSetupProject\bin\Release
files-folder-filter: exe,dll,msi
files-folder-recurse: true
- name: Package Web Service installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- set-zip-directory-web-service
- name: Build c-entron.NET
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- build-centron-net-only
- name: Sign c-entron.NET
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files: |
${{ github.workspace }}\src\centron\Centron.WPF.UI\bin\Release\net10.0-windows\win-x64\publish\c-entron 2.0.exe
${{ github.workspace }}\src\centron\Centron.WPF.UI\bin\Release\net10.0-windows\win-x64\publish\c-entron 2.0.dll
- name: Create c-entron.NET installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- set-dependencies-centron-net build-centron-net-installer-only
- name: Sign c-entron.NET installer
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files-folder: ${{ github.workspace }}\deployment\centron\CentronSetupProject\bin\Release
files-folder-filter: exe,dll,msi
files-folder-recurse: true
- name: Package c-entron.NET installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- set-zip-directory-centron-net
- name: Build Nexus
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Scripts\Scripts.csproj" `
-- build-nexus
- name: Sign Nexus
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files: |
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.Host.exe
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.Host.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.OutlookAddIn.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Core.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.WebServices.Core.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Interfaces.dll
${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Office.Client.dll
- name: Prepare Nexus installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Scripts\Scripts.csproj" `
-- zip-nexus-directory
dotnet tool update --global wix --version 5.0.2 --allow-downgrade
if ($LASTEXITCODE -ne 0) {
dotnet tool install --global wix --version 5.0.2
}
wix extension add -g WixToolset.UI.wixext/5.0.2
dotnet run `
--project ".\scripts\Scripts\Scripts.csproj" `
-- build-nexus-installer
- name: Sign Nexus installer
uses: ./.github/actions/sign-artifacts
with:
azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }}
azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }}
azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }}
files: ${{ github.workspace }}\deployment\WixSharpInstaller\bin\Release\net10.0-windows\c-entron Nexus.msi
- name: Verify signed installers
shell: pwsh
run: |
$files = @(
'.\deployment\centron\WebServiceSetupProject\bin\Release\c-entron Web-Service Installer.msi'
'.\deployment\centron\CentronSetupProject\bin\Release\c-entron.NET Installer.msi'
'.\deployment\WixSharpInstaller\bin\Release\net10.0-windows\c-entron Nexus.msi'
)
foreach ($file in $files) {
if (-not (Test-Path -LiteralPath $file -PathType Leaf)) {
throw "Signed installer not found: $file"
}
$signature = Get-AuthenticodeSignature -LiteralPath $file
if ($signature.Status -ne 'Valid') {
throw "Invalid signature for '$file': $($signature.StatusMessage)"
}
Write-Host "Valid signature: $file"
}
- name: Package Nexus installer
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Scripts\Scripts.csproj" `
-- zip-nexus-singleFile
- name: Upload build artifacts
id: build-artifact
uses: actions/upload-artifact@v7
with:
name: centron-build
path: artifacts/
if-no-files-found: error
retention-days: 14
- name: Upload build version metadata
uses: actions/upload-artifact@v7
with:
name: centron-build-version-${{ steps.version.outputs.version }}
path: ${{ runner.temp }}/centron-build-metadata/version.txt
if-no-files-found: error
retention-days: 14
- name: Add artifact download link
shell: pwsh
run: |
"### Signed build artifacts" >> $env:GITHUB_STEP_SUMMARY
"Build version: ${{ steps.version.outputs.version }}" >> $env:GITHUB_STEP_SUMMARY
"[Download centron-build](${{ steps.build-artifact.outputs.artifact-url }})" >> $env:GITHUB_STEP_SUMMARY
upload-centron-net:
name: Upload c-entron.NET
needs: build
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }}
permissions:
actions: read
contents: read
id-token: write
uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main
with:
artifact_name: centron-build
source_path: c-entron.NET Installer.zip
destination_folder: c-entron.NET
destination_file: c-entron.NET Installer.zip
version: ${{ needs.build.outputs.version }}
main_version: ${{ needs.build.outputs.main_version }}
environment_name: SoftwareBuilds
azure_client_id: ${{ vars.AZURE_CLIENT_ID }}
azure_tenant_id: ${{ vars.AZURE_TENANT_ID }}
upload-web-service:
name: Upload c-entron Web Service
needs: build
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }}
permissions:
actions: read
contents: read
id-token: write
uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main
with:
artifact_name: centron-build
source_path: c-entron Web-Service Installer.zip
destination_folder: c-entron Web-Service
destination_file: c-entron Web-Service Installer.zip
version: ${{ needs.build.outputs.version }}
main_version: ${{ needs.build.outputs.main_version }}
environment_name: SoftwareBuilds
azure_client_id: ${{ vars.AZURE_CLIENT_ID }}
azure_tenant_id: ${{ vars.AZURE_TENANT_ID }}
upload-nexus:
name: Upload c-entron Nexus
needs: build
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }}
permissions:
actions: read
contents: read
id-token: write
uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main
with:
artifact_name: centron-build
source_path: c-entron Nexus Installer.zip
destination_folder: c-entron Nexus
destination_file: c-entron Nexus Installer.zip
version: ${{ needs.build.outputs.version }}
main_version: ${{ needs.build.outputs.main_version }}
environment_name: SoftwareBuilds
azure_client_id: ${{ vars.AZURE_CLIENT_ID }}
azure_tenant_id: ${{ vars.AZURE_TENANT_ID }}
@@ -0,0 +1,117 @@
name: Clean up closed PR artifacts
on:
pull_request_target:
types:
- closed
permissions:
actions: write
pull-requests: read
jobs:
delete-artifacts:
name: Delete PR artifacts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Delete artifacts from PR workflow runs
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;
const pullRequest = context.payload.pull_request;
const workflowRuns = await github.paginate(
github.rest.actions.listWorkflowRunsForRepo,
{
owner,
repo,
event: 'pull_request',
branch: pullRequest.head.ref,
per_page: 100
}
);
const pullRequestRuns = [];
const associatedPullRequestsBySha = new Map();
for (const run of workflowRuns) {
if (!associatedPullRequestsBySha.has(run.head_sha)) {
const associatedPullRequests = await github.paginate(
github.rest.repos.listPullRequestsAssociatedWithCommit,
{
owner,
repo,
commit_sha: run.head_sha,
per_page: 100
}
);
associatedPullRequestsBySha.set(run.head_sha, associatedPullRequests);
}
const belongsToPullRequest = associatedPullRequestsBySha
.get(run.head_sha)
.some(pr => pr.number === pullRequest.number);
if (belongsToPullRequest) {
pullRequestRuns.push(run);
}
}
let deletedArtifacts = 0;
let deletedBytes = 0;
for (const run of pullRequestRuns) {
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts,
{
owner,
repo,
run_id: run.id,
per_page: 100
}
);
for (const artifact of artifacts) {
if (artifact.expired) {
core.info(`Skipping expired artifact ${artifact.name} (${artifact.id}).`);
continue;
}
try {
await github.rest.actions.deleteArtifact({
owner,
repo,
artifact_id: artifact.id
});
deletedArtifacts += 1;
deletedBytes += artifact.size_in_bytes;
core.info(`Deleted artifact ${artifact.name} (${artifact.id}).`);
} catch (error) {
if (error.status === 404) {
core.info(`Artifact ${artifact.name} (${artifact.id}) was already deleted.`);
continue;
}
throw error;
}
}
}
const deletedMiB = (deletedBytes / 1024 / 1024).toFixed(1);
core.info(
`Deleted ${deletedArtifacts} artifact(s) (${deletedMiB} MiB) ` +
`from ${pullRequestRuns.length} workflow run(s) for PR #${pullRequest.number}.`
);
await core.summary
.addHeading('PR artifact cleanup')
.addRaw(`Pull request: #${pullRequest.number}`, true)
.addRaw(`Workflow runs inspected: ${pullRequestRuns.length}`, true)
.addRaw(`Artifacts deleted: ${deletedArtifacts}`, true)
.addRaw(`Storage released: ${deletedMiB} MiB`, true)
.write();
@@ -0,0 +1,269 @@
name: Regression tests
on:
workflow_dispatch:
pull_request:
branches:
- main
- 'release/**'
push:
branches:
- main
- 'release/**'
permissions:
contents: read
# Supersede in-flight runs of this workflow for the same pull request. Pushes to main and
# release branches are excluded so every commit there still gets a full, recorded result.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
regression-tests:
name: End-to-end regression tests
runs-on: ubuntu-24.04
timeout-minutes: 180
env:
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DevExpress_License: ${{ secrets.DEVEXPRESS_LICENSE }}
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
DB_USERNAME: sa
DB_PASSWORD: SA!password
DB_PORT: 1433
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
fetch-depth: 0
# Verifier.OverrideExpectedFiles = true rewrites the expected files instead of comparing
# against them, so a test that keeps the flag silently verifies nothing. It is only meant
# to be set temporarily while regenerating snapshots and must never be committed.
- name: Check for committed OverrideExpectedFiles
shell: pwsh
run: |
$violations = foreach ($file in Get-ChildItem -Path ./tests -Recurse -Filter *.cs) {
$segments = $file.FullName -split '[\\/]'
if ($segments -contains 'bin' -or $segments -contains 'obj') { continue }
$text = Get-Content -LiteralPath $file.FullName -Raw
if ([string]::IsNullOrEmpty($text)) { continue }
# Blank out block comments, keeping newlines so reported line numbers stay correct.
$text = [regex]::Replace($text, '(?s)/\*.*?\*/', { param($m) $m.Value -replace '[^\r\n]', '' })
$lineNumber = 0
foreach ($line in $text -split '\r?\n') {
$lineNumber++
# The assignment must start the line, optionally behind a dotted receiver such as
# this.Verifier. - anything else in front (other code, // or /// ) means it is not a
# statement. Deliberately not stripping // comments: that would also cut a real
# assignment that follows a string containing '//', turning a false positive into a
# false negative.
if ($line -match '^\s*(?:[A-Za-z_][A-Za-z0-9_.]*\.)?OverrideExpectedFiles\s*=\s*true') {
[pscustomobject]@{ Path = $file.FullName; Line = $lineNumber }
}
}
}
if ($violations) {
foreach ($violation in $violations) {
Write-Host "::error file=$($violation.Path),line=$($violation.Line)::Remove 'OverrideExpectedFiles = true' before committing - the test compares nothing while it is set."
}
throw "Found $(@($violations).Count) committed 'OverrideExpectedFiles = true' assignment(s). Regenerate the expected files locally, then remove the flag."
}
Write-Host 'No committed OverrideExpectedFiles assignments found.'
- name: Set up .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Verify test environment
shell: pwsh
run: |
$sdkVersion = dotnet --version
if ($LASTEXITCODE -ne 0 -or $sdkVersion -notmatch '^10\.0\.') {
throw "Expected a .NET 10 SDK, but resolved '$sdkVersion'."
}
docker version
if ($LASTEXITCODE -ne 0) {
throw 'Docker is unavailable on the self-hosted runner.'
}
- name: Sign in to Azure Container Registry
shell: pwsh
run: |
if ([string]::IsNullOrWhiteSpace($env:ACR_USERNAME) -or
[string]::IsNullOrWhiteSpace($env:ACR_PASSWORD)) {
throw 'ACR_USERNAME and ACR_PASSWORD secrets are required on GitHub-hosted runners.'
}
$env:ACR_PASSWORD | docker login centron.azurecr.io --username $env:ACR_USERNAME --password-stdin
if ($LASTEXITCODE -ne 0) {
throw 'Azure Container Registry login failed.'
}
# Superseded runs are now cancelled mid-test. "Stop regression database" below uses
# if: always() and therefore still runs on cancellation, but a hard runner failure can
# leave a container behind. Drop anything older than the job timeout so leftovers cannot
# pile up on the self-hosted runner. Younger containers may belong to a concurrent run of
# another pull request and are left alone.
- name: Remove stale regression containers
continue-on-error: true
shell: pwsh
run: |
$cutoff = (Get-Date).ToUniversalTime().AddHours(-4)
# The docker name filter is a regex over a substring, so an unanchored pattern would
# also match something like backup-centron-regression-db. '^/?' anchors it and works
# whether the daemon matches the bare name or the internal '/name'.
foreach ($id in @(docker ps --all --quiet --filter 'name=^/?centron-regression-')) {
if ([string]::IsNullOrWhiteSpace($id)) { continue }
$parts = (docker inspect --format '{{.Name}}|{{.Created}}' $id) -split '\|', 2
if ($parts.Count -ne 2) { continue }
$name = $parts[0].TrimStart('/')
$created = $parts[1]
# Second guard: this force-removes containers, so never act on a name that does not
# actually carry the prefix, whatever the daemon's filter semantics happen to be.
if (-not $name.StartsWith('centron-regression-')) { continue }
$parsed = [datetime]::MinValue
$isParsed = [datetime]::TryParse(
$created,
[cultureinfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::AdjustToUniversal,
[ref] $parsed)
if ($isParsed -and $parsed -lt $cutoff) {
Write-Host "Removing stale regression container $name created at $created."
docker rm --force $id | Out-Null
}
}
$global:LASTEXITCODE = 0
- name: Start regression database
shell: pwsh
run: |
$containerName = "centron-regression-$env:GITHUB_RUN_ID-$env:GITHUB_RUN_ATTEMPT".ToLowerInvariant()
"DB_CONTAINER_NAME=$containerName" >> $env:GITHUB_ENV
docker pull centron.azurecr.io/centron_db/regression_tests:latest
if ($LASTEXITCODE -ne 0) {
throw 'Could not pull the regression database image.'
}
docker run --detach `
--name $containerName `
--env "MSSQL_SA_PASSWORD=$env:DB_PASSWORD" `
--env ACCEPT_EULA=Y `
--env MSSQL_PID=Standard `
--publish "${env:DB_PORT}:1433" `
centron.azurecr.io/centron_db/regression_tests:latest | Out-Null
if ($LASTEXITCODE -ne 0) {
throw 'Could not start the regression database container.'
}
- name: Wait for regression database
shell: pwsh
run: |
for ($attempt = 1; $attempt -le 120; $attempt++) {
$client = [Net.Sockets.TcpClient]::new()
try {
$connected = $client.ConnectAsync('localhost', [int]$env:DB_PORT).Wait(1000)
if ($connected -and $client.Connected) {
Write-Host 'Regression database is reachable.'
exit 0
}
}
catch {
# Database is still starting.
}
finally {
$client.Dispose()
}
Start-Sleep -Seconds 2
}
throw 'Regression database did not become reachable within four minutes.'
- name: Build regression tests
shell: pwsh
run: |
dotnet build `
"./tests/Centron.Tests.EndToEnd/Centron.Tests.EndToEnd.csproj" `
--configuration Release `
--framework net10.0 `
-nodeReuse:false
- name: Run regression tests
shell: pwsh
env:
CENTRON_TESTS_DATABASE_SERVER: localhost,1433
CENTRON_TESTS_DATABASE_USERNAME: sa
CENTRON_TESTS_DATABASE_PASSWORD: SA!password
DATABASE_BACKUP_PATH: /var/opt/mssql/backup/DatabaseBackup.bak
run: |
dotnet test `
"./tests/Centron.Tests.EndToEnd/Centron.Tests.EndToEnd.csproj" `
--configuration Release `
--framework net10.0 `
--no-build `
--logger "trx;LogFileName=TestResults.trx" `
--results-directory "./artifacts/EndToEndTests" `
-nodeReuse:false
- name: Capture database logs
if: always()
continue-on-error: true
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path './artifacts/EndToEndTests' | Out-Null
docker logs $env:DB_CONTAINER_NAME *>&1 |
Set-Content -LiteralPath './artifacts/EndToEndTests/database.log'
- name: Stop regression database
if: always()
continue-on-error: true
shell: pwsh
run: |
if (-not [string]::IsNullOrWhiteSpace($env:DB_CONTAINER_NAME)) {
docker rm --force $env:DB_CONTAINER_NAME 2>$null
}
$global:LASTEXITCODE = 0
- name: Upload regression results
if: always()
uses: actions/upload-artifact@v7
with:
name: regression-test-results
path: artifacts/EndToEndTests/
if-no-files-found: warn
retention-days: 14
+105
View File
@@ -0,0 +1,105 @@
name: Unit tests
on:
workflow_dispatch:
pull_request:
branches:
- main
- 'release/**'
push:
branches:
- main
- 'release/**'
permissions:
contents: read
# Supersede in-flight runs of this workflow for the same pull request. Pushes to main and
# release branches are excluded so every commit there still gets a full, recorded result.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
name: ${{ matrix.display_name }}
runs-on: windows-2025
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- name: backend-bl
display_name: Backend BL tests
project: .\tests\backend\Centron.Tests.BL\Centron.Tests.BL.csproj
results_directory: artifacts/tests/backend/bl
- name: backend-dao
display_name: Backend DAO tests
project: .\tests\backend\Centron.Tests.DAO\Centron.Tests.DAO.csproj
results_directory: artifacts/tests/backend/dao
- name: shared-core
display_name: Shared Core tests
project: .\tests\shared\Centron.Tests.Core\Centron.Tests.Core.csproj
results_directory: artifacts/tests/shared/core
- name: nexus
display_name: Nexus tests
project: .\tests\CentronNexusTests\CentronNexusTests.csproj
results_directory: artifacts/tests/nexus
env:
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
CENTRON_BUILD_IS_DEV_BUILD: true
DevExpress_License: ${{ secrets.DEVEXPRESS_LICENSE }}
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Verify .NET SDK
shell: pwsh
run: |
$sdkVersion = dotnet --version
if ($LASTEXITCODE -ne 0 -or $sdkVersion -notmatch '^10\.0\.') {
throw "Expected a .NET 10 SDK, but resolved '$sdkVersion'."
}
Write-Host "Resolved .NET SDK: $sdkVersion"
- name: Set build version
shell: pwsh
run: |
dotnet run `
--project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" `
-- setup-versioning
- name: Run tests
shell: pwsh
run: |
dotnet test "${{ matrix.project }}" `
--configuration Release `
--framework net10.0 `
--logger "trx;LogFileName=TestResults.trx" `
--results-directory "${{ matrix.results_directory }}" `
-nodeReuse:false
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: test-results-${{ matrix.name }}
path: ${{ matrix.results_directory }}/TestResults.trx
if-no-files-found: warn
retention-days: 14
+404
View File
@@ -0,0 +1,404 @@
# Created by https://www.gitignore.io/api/visualstudio
# Edit at https://www.gitignore.io/?templates=visualstudio
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.lscache
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# End of https://www.gitignore.io/api/visualstudio
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/.idea
# c-entron Custom Ignores
!src/backend/Centron.Entities/Entities/Import/Log
/deployment/centron/CentronSetupProject/Files
/deployment/centron/WebServiceSetupProject/Files
/deployment/riverbird/RiverbirdWebserviceSetup/Files
/dotnettools
*actual.txt
*AnalyticsUserId.txt
!/nugets/*.nupkg
/.claude/settings.local.json
kind-pike
/.claude/worktrees
BlazorServer/wwwroot/js/index.bundle.js
BlazorServer/NpmJS/src/*.js
BlazorServer/NpmJS/src/*.js.map
BlazorServer/NpmJS/package-lock.json
src/CentronNexus/ProjectManagement/NpmJS/src/*.js
src/CentronNexus/ProjectManagement/NpmJS/src/*.js.map
src/CentronNexus/ProjectManagement/NpmJS/package-lock.json
src/CentronNexus/wwwroot/js/*.bundle.js
src/CentronNexus/Shared/Scripts/src/*.js
src/CentronNexus/Shared/Scripts/src/*.js.map
src/CentronNexus/Shared/Scripts/package-lock.json
/deployment/WixSharpInstaller/Files
/src/CentronNexus/.config/dotnet-tools.json
/src/CentronNexus/Microsoft.CodeAnalysis.Razor.Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/
/.windows-mcp
+26
View File
@@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/centron/Centron.WPF.UI/bin/Debug/net8.0-windows/c-entron 2.0.dll",
"args": [],
"cwd": "${workspaceFolder}/src/centron/Centron.WPF.UI",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/centron/Centron.WPF.UI/Centron.WPF.UI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/centron/Centron.WPF.UI/Centron.WPF.UI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/src/centron/Centron.WPF.UI/Centron.WPF.UI.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\backend\Centron.Interfaces\Centron.Interfaces.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,131 @@
using Centron.Api.docuFORM.Helper;
using Centron.Api.docuFORM.Models;
using Centron.Api.docuFORM.Models.Swagger;
using Centron.Api.docuFORM.Models.Swagger.Responses;
using Centron.Core;
using Centron.Core.Extensions;
using Centron.Interfaces.BL;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM
{
public class DocuFormRestApiClient : IDocuFormApiClient, IDisposable
{
#region Fields
private readonly HttpClient _httpClient;
private bool _disposed = false;
#endregion
#region Ctor
public DocuFormRestApiClient(string serverAddress)
{
try
{
var trimmedAddress = serverAddress.TrimEnd('/');
var handler = new SocketsHttpHandler() { MaxConnectionsPerServer = 20 };
this._httpClient = new HttpClient(handler) { BaseAddress = new Uri(trimmedAddress) };
this._httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
catch(Exception ex)
{
throw new Exception($"Fehler beim Initalisieren des HTTP-Clients für folgende Adresse \"{serverAddress}\"!{Environment.NewLine}{ex.Message}");
}
}
#endregion
#region Methods
public async Task<string> RequestAuthorization(AuthCodeRequest authCodeRequest)
{
Guard.NotNull(authCodeRequest, nameof(authCodeRequest));
var requestUrl = DocuFormRequestHelper.CreateAuthorizationRequestURI(authCodeRequest);
var response = await this._httpClient.GetAsync(requestUrl);
var contentString = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException($"Fehler während der Authorisierung bei docuFORM: {contentString}");
return contentString;
}
public async Task<AuthTokenResponse> RequestToken(AuthTokenRequest tokenRequest)
{
Guard.NotNull(tokenRequest, nameof(tokenRequest));
var paremeters = DocuFormRequestHelper.RequestToParameters(tokenRequest);
var request = new HttpRequestMessage(HttpMethod.Post, DocuFormRestApiConstants._endpointAuthToken)
{
Content = new FormUrlEncodedContent(paremeters)
};
var response = await this._httpClient.SendAsync(request);
var responseContentString = await response.ReadContentAndThrowIfError();
var authToken = JsonSerializer.Deserialize<AuthTokenResponse>(responseContentString);
if(authToken == null)
throw new HttpRequestException($"No auth token was received!");
return authToken;
}
public async Task<Result<DevicesResponse>> GetAllDevices(string token)
{
var requestUri = DocuFormRestApiConstants._endpointDevices;
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await this._httpClient.SendRequestWithToken(request, token);
return await response.CheckResponseAndDeserializeContent<DevicesResponse>();
}
public async Task<Result<DeviceCountersResponse>> GetDeviceCounters(string token, int deviceId, DateTime? requestedDate = null)
{
var requestUri = DocuFormRestApiConstants._endpointDevices + $"/{deviceId}/counters";
if (requestedDate.HasValue)
{
var utcDate = requestedDate.Value.ToUniversalTime();
requestUri += $"?date={utcDate.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)}";
}
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await this._httpClient.SendRequestWithToken(request, token);
return await response.CheckResponseAndDeserializeContent<DeviceCountersResponse>();
}
#endregion
#region IDisposable Support
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed && disposing)
{
_httpClient?.Dispose();
_disposed = true;
}
}
#endregion
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Centron.Api.docuFORM
{
internal static class DocuFormRestApiConstants
{
internal static readonly string _redirectURI = "http://127.0.0.1"; // Don't ad trailing /
internal static readonly string _endpointAuthToken = "/auth/v2/token";
internal static readonly string _endpointAuthCode = "/auth/v2/authorize";
internal static readonly string _endpointDevices = "/dfmserver/v2/devices";
}
}
@@ -0,0 +1,70 @@
using Centron.Api.docuFORM.Models;
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Helper
{
public static class DocuFormRequestHelper
{
public static string CreateAuthorizationRequestURI(AuthCodeRequest authCodeRequest, string? baseUriToAdd = null)
{
var parameters = RequestToParameterString(authCodeRequest);
var endPointAndParameters = DocuFormRestApiConstants._endpointAuthCode + "?" + parameters;
var baseUri = baseUriToAdd != null
? new Uri(baseUriToAdd)
: null;
return baseUri != null
? new Uri(baseUri, endPointAndParameters).ToString()
: endPointAndParameters;
}
public static string RequestToParameterString<T>(T request)
{
var parameters = RequestToParameters(request);
var parameterString = string.Join("&", parameters.Select(f => $"{WebUtility.UrlEncode(f.Key)}={WebUtility.UrlEncode(f.Value)}"));
return parameterString;
}
public static Dictionary<string, string> RequestToParameters<T>(T request, bool removeParameterWithoutValue = true)
{
var dict = new Dictionary<string, string>();
var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
var attr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
var key = attr?.Name ?? prop.Name;
var value = prop.GetValue(request)?.ToString();
if(removeParameterWithoutValue && string.IsNullOrWhiteSpace(value))
continue;
dict[key] = value ?? string.Empty;
}
return dict;
}
public static string CreateRedirectURI(int? portNumber = null)
{
var uriBuilder = new UriBuilder(DocuFormRestApiConstants._redirectURI);
uriBuilder.Port = portNumber.HasValue && portNumber.Value > 0
? portNumber.Value
: -1;
var uri = uriBuilder.ToString();
while(uri.EndsWith("/"))
uri = uri.Substring(0, uri.Length - 1);
return uri;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Text;
namespace Centron.Api.docuFORM.Helper
{
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> SendRequestWithToken(this HttpClient httpClient, HttpRequestMessage httpRequest, string token)
{
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
if (httpRequest.Headers.Accept.Count == 0)
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await httpClient.SendAsync(httpRequest).ConfigureAwait(false);
}
}
}
@@ -0,0 +1,50 @@
using Centron.Api.docuFORM.Models.Swagger.Responses;
using Centron.Interfaces.BL;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.Json;
namespace Centron.Api.docuFORM.Helper
{
public static class HttpResponseMessageExtensions
{
public static async Task<string> ReadContentAndThrowIfError(this HttpResponseMessage? response)
{
if(response == null)
throw new HttpRequestException ($"No http response received!");
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException ($"An error occured during the http request: {content}");
return content;
}
public static async Task<Result<T>> CheckResponseAndDeserializeContent<T>(this HttpResponseMessage? response)
{
try
{
if(response == null)
throw new HttpRequestException ($"No http response received!");
var content = await ReadContentAndThrowIfError(response);
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var obj = JsonSerializer.Deserialize<T>(content, options);
if(obj != null)
return Result<T>.AsSuccess(obj);
return Result<T>.AsError("Unable to deserialize object!");
}
catch (Exception ex)
{
return Result<T>.FromException(ex);
}
}
}
}
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace Centron.Api.docuFORM.Helper
{
public static class OAuthHelper
{
// Can be used to generate a "state" or the codeVerifier for the "code challenge"
public static string GenerateRandomBase64String(int byteLength = 32)
{
byte[] randomBytes = new byte[byteLength];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
// Base64 URL-safe encoding
string state = Convert.ToBase64String(randomBytes)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "");
return state;
}
public static string GenerateCodeChallenge(string codeVerifier)
{
using (var sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.ASCII.GetBytes(codeVerifier));
string codeChallenge = Convert.ToBase64String(hash)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "");
return codeChallenge;
}
}
}
}
@@ -0,0 +1,21 @@
using Centron.Api.docuFORM.Models;
using Centron.Api.docuFORM.Models.Swagger;
using Centron.Api.docuFORM.Models.Swagger.Responses;
using Centron.Interfaces.BL;
using System;
using System.Collections.Generic;
using System.Text;
namespace Centron.Api.docuFORM
{
public interface IDocuFormApiClient
{
public Task<string> RequestAuthorization(AuthCodeRequest authCodeRequest);
public Task<AuthTokenResponse> RequestToken(AuthTokenRequest tokenRequest);
public Task<Result<DevicesResponse>> GetAllDevices(string token);
public Task<Result<DeviceCountersResponse>> GetDeviceCounters(string token, int deviceId, DateTime? requestedDate);
}
}
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models
{
public class AuthCodeRequest
{
[JsonPropertyName("client_id")]
public string ClientId { get; set; } = string.Empty;
[JsonPropertyName("response_type")]
public string ResponseType { get; set; } = "code";
[JsonPropertyName("scope")]
public string Scope { get; set; } = string.Empty;
[JsonPropertyName("redirect_uri")]
public string RedirectUri { get; set; } = "http://127.0.0.1";
[JsonPropertyName("authMode")]
public string AuthMode { get; set; } = string.Empty;
[JsonPropertyName("state")]
public string State { get; set; } = string.Empty;
[JsonPropertyName("code_challenge_method")]
public string CodeChallengeMethod { get; set; } = "S256";
[JsonPropertyName("code_challenge")]
public string CodeChallenge { get; set; } = string.Empty;
[JsonPropertyName("infotext")]
public string InfoText { get; set; } = string.Empty;
[JsonPropertyName("username")]
public string UserName { get; set; } = string.Empty;
}
}
@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class AuthTokenRequest
{
[JsonPropertyName("grant_type")]
public string GrantType { get; set; } = string.Empty;
[JsonPropertyName("client_id")]
public string ClientId { get; set; } = string.Empty;
[JsonPropertyName("client_secret")]
public string? ClientSecret { get; set; }
[JsonPropertyName("username")]
public string? Username { get; set; }
[JsonPropertyName("password")]
public string? Password { get; set; }
[JsonPropertyName("refresh_token")]
public string? RefreshToken { get; set; }
[JsonPropertyName("code")]
public string? Code { get; set; }
[JsonPropertyName("code_verifier")]
public string? CodeVerifier { get; set; }
[JsonPropertyName("scope")]
public string? Scope { get; set; }
[JsonPropertyName("redirect_uri")]
public string? RedirectUri { get; set; }
[JsonPropertyName("client_deviceid")]
public string? ClientDeviceId { get; set; }
}
@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class AuthTokenResponse
{
[JsonPropertyName("access_token")]
public string? AccessToken { get; set; }
[JsonPropertyName("refresh_token")]
public string? RefreshToken { get; set; }
[JsonPropertyName("expires_in")]
public long ExpiresIn { get; set; }
[JsonPropertyName("token_type")]
public string? TokenType { get; set; }
[JsonPropertyName("server_api_url")]
public string? ServerApiUrl { get; set; }
[JsonPropertyName("client_api_url")]
public string? ClientApiUrl { get; set; }
[JsonPropertyName("user_id")]
public int UserId { get; set; }
}
@@ -0,0 +1,30 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Customer : CustomerCreate
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("uuid")]
public string? Uuid { get; set; }
[JsonPropertyName("dealerUuid")]
public string? DealerUuid { get; set; }
[JsonPropertyName("created")]
public DateTime? Created { get; set; }
[JsonPropertyName("createdBy")]
public string? CreatedBy { get; set; }
[JsonPropertyName("restApiClientUuid")]
public string? RestApiClientUuid { get; set; }
[JsonPropertyName("lastLogin")]
public DateTime? LastLogin { get; set; }
[JsonPropertyName("productVersion")]
public string? ProductVersion { get; set; }
}
@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class CustomerCreate : CustomerUpdate
{
[JsonPropertyName("dealerId")]
public int DealerId { get; set; }
/// <summary>Customer name. Shadows <see cref="UserProperties.Name"/> (the user login name).</summary>
[JsonPropertyName("name")]
public new string Name { get; set; } = string.Empty;
}
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class CustomerDeployment
{
[JsonPropertyName("mcsCollectionUuid")]
public string? McsCollectionUuid { get; set; }
[JsonPropertyName("mcsPassword")]
public string? McsPassword { get; set; }
[JsonPropertyName("mcsOauthHost")]
public string? McsOauthHost { get; set; }
}
@@ -0,0 +1,30 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class CustomerRights
{
[JsonPropertyName("dashboard")]
public bool? Dashboard { get; set; }
[JsonPropertyName("reports")]
public bool? Reports { get; set; }
[JsonPropertyName("eshop")]
public bool? Eshop { get; set; }
[JsonPropertyName("customerThresholds")]
public bool? CustomerThresholds { get; set; }
[JsonPropertyName("customerAlerts")]
public bool? CustomerAlerts { get; set; }
[JsonPropertyName("deletePrinters")]
public bool? DeletePrinters { get; set; }
[JsonPropertyName("contractManagement")]
public bool? ContractManagement { get; set; }
[JsonPropertyName("deployment")]
public bool? Deployment { get; set; }
}
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class CustomerUpdate : UserProperties
{
[JsonPropertyName("password")]
public string? Password { get; set; }
[JsonPropertyName("submitPassword")]
public string? SubmitPassword { get; set; }
[JsonPropertyName("f2pControl")]
public string? F2pControl { get; set; }
[JsonPropertyName("rights")]
public CustomerRights? Rights { get; set; }
[JsonPropertyName("deployment")]
public CustomerDeployment? Deployment { get; set; }
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Dealer : UserProperties
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("uuid")]
public string? Uuid { get; set; }
[JsonPropertyName("created")]
public DateTime? Created { get; set; }
[JsonPropertyName("createdBy")]
public string? CreatedBy { get; set; }
[JsonPropertyName("restApiClientUuid")]
public string? RestApiClientUuid { get; set; }
[JsonPropertyName("lastLogin")]
public DateTime? LastLogin { get; set; }
}
@@ -0,0 +1,73 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Device : DeviceCreate
{
// DeviceProperties (server-populated read fields)
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("uuid")]
public string? Uuid { get; set; }
[JsonPropertyName("macAddress")]
public string? MacAddress { get; set; }
[JsonPropertyName("serialNumber")]
public string? SerialNumber { get; set; }
[JsonPropertyName("ipAddress")]
public string? IpAddress { get; set; }
[JsonPropertyName("hostAddressDate")]
public DateTime? HostAddressDate { get; set; }
[JsonPropertyName("contractId")]
public int? ContractId { get; set; }
[JsonPropertyName("createdOn")]
public DeviceCreatedOn? CreatedOn { get; set; }
[JsonPropertyName("firmware")]
public string? Firmware { get; set; }
[JsonPropertyName("mibFile")]
public DeviceMibFile? MibFile { get; set; }
[JsonPropertyName("modelName")]
public string? ModelName { get; set; }
[JsonPropertyName("vendorName")]
public string? VendorName { get; set; }
[JsonPropertyName("vendorId")]
public int? VendorId { get; set; }
[JsonPropertyName("systemName")]
public string? SystemName { get; set; }
[JsonPropertyName("pageCount")]
public int? PageCount { get; set; }
[JsonPropertyName("properties")]
public List<string>? Properties { get; set; }
[JsonPropertyName("outputDimensions")]
public DeviceFeatureOutputDimensions? OutputDimensions { get; set; }
[JsonPropertyName("technicalSpecs")]
public DeviceFeatureTecSpecs? TechnicalSpecs { get; set; }
[JsonPropertyName("numOutputTrays")]
public int? NumOutputTrays { get; set; }
[JsonPropertyName("numPaperTrays")]
public int? NumPaperTrays { get; set; }
[JsonPropertyName("resolution")]
public string? Resolution { get; set; }
[JsonPropertyName("paperTrays")]
public List<DevicePaperTray>? PaperTrays { get; set; }
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceCiData
{
[JsonPropertyName("articleNumber")]
public string? ArticleNumber { get; set; }
[JsonPropertyName("ciSerial")]
public string? CiSerial { get; set; }
[JsonPropertyName("exceptionFleet")]
public bool ExceptionFleet { get; set; }
[JsonPropertyName("mpsClientId")]
public string? MpsClientId { get; set; }
[JsonPropertyName("npsServerId")]
public string? NpsServerId { get; set; }
[JsonPropertyName("sla")]
public string? Sla { get; set; }
}
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceCounters
{
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("timestamp")]
public DateTime? Timestamp { get; set; }
[JsonPropertyName("standardCounters")]
public Dictionary<string, int>? StandardCounters { get; set; }
[JsonPropertyName("extendedCounters")]
public Dictionary<string, int>? ExtendedCounters { get; set; }
}
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceCreate : DeviceUpdate
{
[JsonPropertyName("dealerId")]
public int DealerId { get; set; }
[JsonPropertyName("customerId")]
public int CustomerId { get; set; }
[JsonPropertyName("hostAddress")]
public string HostAddress { get; set; } = string.Empty;
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceCreatedOn
{
[JsonPropertyName("client")]
public DateTime? Client { get; set; }
[JsonPropertyName("server")]
public DateTime? Server { get; set; }
}
@@ -0,0 +1,42 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceEvent
{
[JsonPropertyName("id")]
public int? Id { get; set; }
[JsonPropertyName("eventId")]
public string? EventId { get; set; }
[JsonPropertyName("time")]
public DateTime? Time { get; set; }
[JsonPropertyName("notified")]
public bool? Notified { get; set; }
[JsonPropertyName("clearDate")]
public DateTime? ClearDate { get; set; }
[JsonPropertyName("contractId")]
public int? ContractId { get; set; }
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;
[JsonPropertyName("key")]
public string? Key { get; set; }
[JsonPropertyName("pageCount")]
public int? PageCount { get; set; }
[JsonPropertyName("severity")]
public string? Severity { get; set; }
[JsonPropertyName("source")]
public string? Source { get; set; }
}
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceEvents
{
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("events")]
public List<DeviceEvent>? Events { get; set; }
[JsonPropertyName("nextPageToken")]
public string? NextPageToken { get; set; }
}
@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
/// <summary>Media feed dimensions of the device.</summary>
public class DeviceFeatureOutputDimensions
{
[JsonPropertyName("maxMediaFeedDimension")]
public long? MaxMediaFeedDimension { get; set; }
[JsonPropertyName("maxMediaXFeedDimension")]
public long? MaxMediaXFeedDimension { get; set; }
[JsonPropertyName("northMargin")]
public long? NorthMargin { get; set; }
}
@@ -0,0 +1,28 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
/// <summary>Technical specifications of the device.</summary>
public class DeviceFeatureTecSpecs
{
[JsonPropertyName("copiesByDriver")]
public int? CopiesByDriver { get; set; }
[JsonPropertyName("externalPSGeneration")]
public int? ExternalPsGeneration { get; set; }
[JsonPropertyName("feedResolution")]
public int? FeedResolution { get; set; }
[JsonPropertyName("paperTrayFormatIds")]
public string? PaperTrayFormatIds { get; set; }
[JsonPropertyName("securePinPrinting")]
public string? SecurePinPrinting { get; set; }
[JsonPropertyName("supportedPDL")]
public string? SupportedPdl { get; set; }
[JsonPropertyName("xFeedResolution")]
public int? XFeedResolution { get; set; }
}
@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceFeatures
{
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("properties")]
public List<string>? Properties { get; set; }
[JsonPropertyName("outputDimensions")]
public DeviceFeatureOutputDimensions? OutputDimensions { get; set; }
[JsonPropertyName("technicalSpecs")]
public DeviceFeatureTecSpecs? TechnicalSpecs { get; set; }
[JsonPropertyName("outputTrays")]
public int? OutputTrays { get; set; }
[JsonPropertyName("paperTrays")]
public int? PaperTrays { get; set; }
[JsonPropertyName("resolution")]
public string? Resolution { get; set; }
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceItem
{
[JsonPropertyName("supplyIndex")]
public int SupplyIndex { get; set; }
[JsonPropertyName("installationIncluded")]
public bool? InstallationIncluded { get; set; }
[JsonPropertyName("isRecommended")]
public bool? IsRecommended { get; set; }
[JsonPropertyName("isAttached")]
public bool? IsAttached { get; set; }
[JsonPropertyName("isOriginal")]
public bool? IsOriginal { get; set; }
[JsonPropertyName("item")]
public Item? Item { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceItemCreate : ItemCreateProperties
{
[JsonPropertyName("supplyIndex")]
public int SupplyIndex { get; set; }
[JsonPropertyName("installationIncluded")]
public bool? InstallationIncluded { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceMibFile
{
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("version")]
public string? Version { get; set; }
}
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceNotifications
{
[JsonPropertyName("alertName")]
public string? AlertName { get; set; }
[JsonPropertyName("thresholdName")]
public string? ThresholdName { get; set; }
[JsonPropertyName("thresholdAction")]
public int? ThresholdAction { get; set; }
/// <summary>Note: the API uses "tresholdReceiver" (typo) in the write schema but "thresholdReceiver" in read.</summary>
[JsonPropertyName("thresholdReceiver")]
public string? ThresholdReceiver { get; set; }
[JsonPropertyName("thresholdSubject")]
public string? ThresholdSubject { get; set; }
}
@@ -0,0 +1,30 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DevicePaperTray
{
[JsonPropertyName("index")]
public int Index { get; set; }
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("description")]
public string? Description { get; set; }
[JsonPropertyName("formatId")]
public int? FormatId { get; set; }
[JsonPropertyName("formatName")]
public string? FormatName { get; set; }
[JsonPropertyName("mediaDimX")]
public int? MediaDimX { get; set; }
[JsonPropertyName("mediaDimY")]
public int? MediaDimY { get; set; }
[JsonPropertyName("mediaLevelPcnt")]
public int? MediaLevelPcnt { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DevicePaperTrays
{
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("paperTrays")]
public List<DevicePaperTray>? PaperTrays { get; set; }
}
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
/// <summary>SNMP configuration as returned in Device read responses.</summary>
public class DeviceSnmp
{
[JsonPropertyName("community")]
public string? Community { get; set; }
[JsonPropertyName("contextName")]
public string? ContextName { get; set; }
[JsonPropertyName("portNumber")]
public int? PortNumber { get; set; }
[JsonPropertyName("securityName")]
public string? SecurityName { get; set; }
[JsonPropertyName("version")]
public int? Version { get; set; }
}
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSnmpData
{
[JsonPropertyName("contact")]
public string? Contact { get; set; }
[JsonPropertyName("location")]
public string? Location { get; set; }
[JsonPropertyName("overwritable")]
public bool Overwritable { get; set; }
[JsonPropertyName("queriedOn")]
public DateTime? QueriedOn { get; set; }
}
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSummary
{
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("lastUpdate")]
public DateTime? LastUpdate { get; set; }
[JsonPropertyName("warning")]
public bool Warning { get; set; }
[JsonPropertyName("groups")]
public List<DeviceSummaryGroup>? Groups { get; set; }
}
@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSummaryCounterElement
{
/// <summary>Type discriminator — value is 2 for counter elements.</summary>
[JsonPropertyName("type")]
public int? Type { get; set; }
[JsonPropertyName("title")]
public string? Title { get; set; }
[JsonPropertyName("value")]
public string? Value { get; set; }
}
@@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSummaryGroup
{
[JsonPropertyName("title")]
public string? Title { get; set; }
/// <summary>
/// Each element is either a <see cref="DeviceSummarySupplyElement"/> (type=1) or a
/// <see cref="DeviceSummaryCounterElement"/> (type=2). Deserialized as generic objects;
/// check the "type" field to cast accordingly.
/// </summary>
[JsonPropertyName("elements")]
public List<object>? Elements { get; set; }
}
@@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSummarySupplyElement
{
/// <summary>Type discriminator — value is 1 for supply elements.</summary>
[JsonPropertyName("type")]
public int? Type { get; set; }
[JsonPropertyName("hex")]
public string? Hex { get; set; }
[JsonPropertyName("title")]
public string? Title { get; set; }
[JsonPropertyName("subtitle")]
public string? Subtitle { get; set; }
[JsonPropertyName("warning")]
public bool? Warning { get; set; }
[JsonPropertyName("value")]
public string? Value { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSupplies
{
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("supplies")]
public List<DeviceSupply>? Supplies { get; set; }
}
@@ -0,0 +1,60 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceSupply
{
[JsonPropertyName("attachedItemId")]
public int? AttachedItemId { get; set; }
[JsonPropertyName("capacity")]
public int? Capacity { get; set; }
[JsonPropertyName("class")]
public int? Class { get; set; }
[JsonPropertyName("colorant")]
public string? Colorant { get; set; }
[JsonPropertyName("index")]
public int Index { get; set; }
[JsonPropertyName("level")]
public int? Level { get; set; }
[JsonPropertyName("levelCorrectionMode")]
public string? LevelCorrectionMode { get; set; }
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("orderState")]
public string? OrderState { get; set; }
[JsonPropertyName("originalPartNo")]
public string? OriginalPartNo { get; set; }
[JsonPropertyName("percent")]
public int? Percent { get; set; }
[JsonPropertyName("remainingPages")]
public int? RemainingPages { get; set; }
[JsonPropertyName("remainingDays")]
public int? RemainingDays { get; set; }
[JsonPropertyName("thresholdReached")]
public bool? ThresholdReached { get; set; }
[JsonPropertyName("timestamp")]
public DateTime? Timestamp { get; set; }
[JsonPropertyName("type")]
public int? Type { get; set; }
[JsonPropertyName("unit")]
public int? Unit { get; set; }
[JsonPropertyName("value")]
public string? Value { get; set; }
}
@@ -0,0 +1,78 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceUpdate
{
[JsonPropertyName("isActive")]
public bool? IsActive { get; set; }
[JsonPropertyName("isManaged")]
public bool? IsManaged { get; set; }
[JsonPropertyName("isLicensed")]
public bool? IsLicensed { get; set; }
[JsonPropertyName("assetNumber")]
public string? AssetNumber { get; set; }
[JsonPropertyName("cardReaderAddress")]
public string? CardReaderAddress { get; set; }
[JsonPropertyName("ciid")]
public string? Ciid { get; set; }
[JsonPropertyName("ciData")]
public DeviceCiData? CiData { get; set; }
[JsonPropertyName("costCenter")]
public string? CostCenter { get; set; }
[JsonPropertyName("description")]
public string? Description { get; set; }
[JsonPropertyName("follow2Print")]
public bool? Follow2Print { get; set; }
[JsonPropertyName("information")]
public List<string>? Information { get; set; }
[JsonPropertyName("inventoryNumber")]
public string? InventoryNumber { get; set; }
[JsonPropertyName("itemDeliveryAddress")]
public string? ItemDeliveryAddress { get; set; }
[JsonPropertyName("itemDeliveryNote")]
public string? ItemDeliveryNote { get; set; }
[JsonPropertyName("itemDeliveryInformation")]
public List<string>? ItemDeliveryInformation { get; set; }
[JsonPropertyName("locationDescription")]
public string? LocationDescription { get; set; }
[JsonPropertyName("locationLevels")]
public List<string>? LocationLevels { get; set; }
[JsonPropertyName("notifications")]
public DeviceNotifications? Notifications { get; set; }
[JsonPropertyName("officePrint")]
public bool? OfficePrint { get; set; }
[JsonPropertyName("productionProcess")]
public bool? ProductionProcess { get; set; }
[JsonPropertyName("securePrint")]
public bool? SecurePrint { get; set; }
[JsonPropertyName("snmp")]
public DeviceSnmp? Snmp { get; set; }
[JsonPropertyName("snmpData")]
public DeviceSnmpData? SnmpData { get; set; }
[JsonPropertyName("warranty")]
public DeviceWarranty? Warranty { get; set; }
}
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class DeviceWarranty
{
[JsonPropertyName("start")]
public DateTime? Start { get; set; }
[JsonPropertyName("months")]
public int? Months { get; set; }
[JsonPropertyName("clicks")]
public int? Clicks { get; set; }
}
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Item : ItemCreateProperties
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("colorant")]
public string? Colorant { get; set; }
[JsonPropertyName("supplyType")]
public int? SupplyType { get; set; }
[JsonPropertyName("isColorMarker")]
public bool? IsColorMarker { get; set; }
}
@@ -0,0 +1,42 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class ItemCreateProperties
{
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
[JsonPropertyName("description")]
public string? Description { get; set; }
[JsonPropertyName("manufacturer")]
public string? Manufacturer { get; set; }
[JsonPropertyName("partNo")]
public string PartNo { get; set; } = string.Empty;
[JsonPropertyName("articleNo")]
public string ArticleNo { get; set; } = string.Empty;
[JsonPropertyName("originalPartNo")]
public string OriginalPartNo { get; set; } = string.Empty;
[JsonPropertyName("isKit")]
public bool? IsKit { get; set; }
[JsonPropertyName("pages")]
public int? Pages { get; set; }
[JsonPropertyName("percentage")]
public double? Percentage { get; set; }
[JsonPropertyName("price")]
public double? Price { get; set; }
[JsonPropertyName("currency")]
public string? Currency { get; set; }
[JsonPropertyName("unit")]
public string? Unit { get; set; }
}
@@ -0,0 +1,35 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class JobCreate
{
[JsonPropertyName("ipAddress")]
public string IpAddress { get; set; } = string.Empty;
[JsonPropertyName("cardId")]
public string CardId { get; set; } = string.Empty;
/// <summary>1=SmartCard, 2=PIN, 3=Fingerprint, 4=Username.</summary>
[JsonPropertyName("cardType")]
public int CardType { get; set; }
[JsonPropertyName("jobName")]
public string JobName { get; set; } = string.Empty;
[JsonPropertyName("billingCode")]
public int? BillingCode { get; set; }
/// <summary>20=Copies, 21=Scan, 22=Fax.</summary>
[JsonPropertyName("dataType")]
public int DataType { get; set; }
[JsonPropertyName("stapled")]
public bool? Stapled { get; set; }
[JsonPropertyName("punched")]
public bool? Punched { get; set; }
[JsonPropertyName("paperInfos")]
public List<JobCreatePaperInfo>? PaperInfos { get; set; }
}
@@ -0,0 +1,30 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class JobCreatePaperInfo
{
[JsonPropertyName("paperTray")]
public int? PaperTray { get; set; }
[JsonPropertyName("windowsPaperSize")]
public int? WindowsPaperSize { get; set; }
[JsonPropertyName("paperWidth")]
public int? PaperWidth { get; set; }
[JsonPropertyName("paperHeight")]
public int? PaperHeight { get; set; }
[JsonPropertyName("pagesSimplexBw")]
public int PagesSimplexBw { get; set; }
[JsonPropertyName("pagesDuplexBw")]
public int PagesDuplexBw { get; set; }
[JsonPropertyName("pagesSimplexColor")]
public int PagesSimplexColor { get; set; }
[JsonPropertyName("pagesDuplexColor")]
public int PagesDuplexColor { get; set; }
}
@@ -0,0 +1,51 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class JobRead
{
[JsonPropertyName("access")]
public bool Access { get; set; }
[JsonPropertyName("balance")]
public double? Balance { get; set; }
[JsonPropertyName("billing")]
public bool? Billing { get; set; }
[JsonPropertyName("billingId")]
public int? BillingId { get; set; }
[JsonPropertyName("budget")]
public double? Budget { get; set; }
[JsonPropertyName("clickTimeout")]
public int? ClickTimeout { get; set; }
[JsonPropertyName("currency")]
public string? Currency { get; set; }
[JsonPropertyName("email")]
public string? Email { get; set; }
[JsonPropertyName("fullName")]
public string? FullName { get; set; }
[JsonPropertyName("maxTotalCount")]
public int? MaxTotalCount { get; set; }
[JsonPropertyName("maxColorCount")]
public int? MaxColorCount { get; set; }
[JsonPropertyName("private")]
public bool? Private { get; set; }
[JsonPropertyName("senseTimeout")]
public int? SenseTimeout { get; set; }
[JsonPropertyName("userId")]
public int? UserId { get; set; }
[JsonPropertyName("userName")]
public string? UserName { get; set; }
}
@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class MpsClient
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("uuid")]
public string? Uuid { get; set; }
[JsonPropertyName("dealerId")]
public int DealerId { get; set; }
[JsonPropertyName("clientApiUrl")]
public string? ClientApiUrl { get; set; }
[JsonPropertyName("lastTransfer")]
public DateTime? LastTransfer { get; set; }
[JsonPropertyName("lastUpdate")]
public DateTime? LastUpdate { get; set; }
[JsonPropertyName("lastUpdateQuery")]
public DateTime? LastUpdateQuery { get; set; }
}
@@ -0,0 +1,67 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Order
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("orderNumber")]
public string? OrderNumber { get; set; }
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("supplyIndex")]
public int? SupplyIndex { get; set; }
[JsonPropertyName("quantity")]
public int? Quantity { get; set; }
[JsonPropertyName("purchaseRequisitionNumber")]
public string? PurchaseRequisitionNumber { get; set; }
[JsonPropertyName("installationIncluded")]
public bool? InstallationIncluded { get; set; }
[JsonPropertyName("note")]
public string? Note { get; set; }
[JsonPropertyName("orderDate")]
public DateTime? OrderDate { get; set; }
[JsonPropertyName("orderedBy")]
public string? OrderedBy { get; set; }
[JsonPropertyName("deliveryDate")]
public DateTime? DeliveryDate { get; set; }
[JsonPropertyName("address")]
public string? Address { get; set; }
[JsonPropertyName("deliveryNotes")]
public List<string>? DeliveryNotes { get; set; }
[JsonPropertyName("supplierInfoUrl")]
public string? SupplierInfoUrl { get; set; }
[JsonPropertyName("item")]
public Item? Item { get; set; }
/// <summary>Current order state: notAvailable, proposed, placed, confirmed, shipped, delivered, completed.</summary>
[JsonPropertyName("state")]
public string? State { get; set; }
[JsonPropertyName("requestDate")]
public DateTime? RequestDate { get; set; }
[JsonPropertyName("requestedBy")]
public string? RequestedBy { get; set; }
[JsonPropertyName("mountDate")]
public DateTime? MountDate { get; set; }
[JsonPropertyName("shipmentInfo")]
public OrderShipmentInfo? ShipmentInfo { get; set; }
}
@@ -0,0 +1,64 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class OrderCreate
{
// Required fields
[JsonPropertyName("deviceId")]
public int DeviceId { get; set; }
[JsonPropertyName("supplyIndex")]
public int SupplyIndex { get; set; }
[JsonPropertyName("quantity")]
public int Quantity { get; set; }
/// <summary>Required. Allowed values: placed, confirmed, shipped, delivered.</summary>
[JsonPropertyName("state")]
public string State { get; set; } = string.Empty;
// Item identification (at least one required)
[JsonPropertyName("itemId")]
public int? ItemId { get; set; }
[JsonPropertyName("articleNo")]
public string? ArticleNo { get; set; }
[JsonPropertyName("partNo")]
public string? PartNo { get; set; }
// Order details
[JsonPropertyName("orderNumber")]
public string? OrderNumber { get; set; }
[JsonPropertyName("purchaseRequisitionNumber")]
public string? PurchaseRequisitionNumber { get; set; }
[JsonPropertyName("installationIncluded")]
public bool? InstallationIncluded { get; set; }
[JsonPropertyName("note")]
public string? Note { get; set; }
[JsonPropertyName("orderDate")]
public DateTime? OrderDate { get; set; }
[JsonPropertyName("orderedBy")]
public string? OrderedBy { get; set; }
[JsonPropertyName("deliveryDate")]
public DateTime? DeliveryDate { get; set; }
[JsonPropertyName("address")]
public string? Address { get; set; }
[JsonPropertyName("deliveryNotes")]
public List<string>? DeliveryNotes { get; set; }
[JsonPropertyName("supplierInfoUrl")]
public string? SupplierInfoUrl { get; set; }
[JsonPropertyName("shipmentInfo")]
public OrderShipmentInfo? ShipmentInfo { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class OrderShipmentInfo
{
[JsonPropertyName("text")]
public string? Text { get; set; }
[JsonPropertyName("url")]
public string? Url { get; set; }
}
@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class OrderUpdate
{
/// <summary>Allowed values: notAvailable, confirmed, shipped, delivered.</summary>
[JsonPropertyName("state")]
public string? State { get; set; }
[JsonPropertyName("deliveryDate")]
public DateTime? DeliveryDate { get; set; }
[JsonPropertyName("shipmentInfo")]
public OrderShipmentInfo? ShipmentInfo { get; set; }
}
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class ResourceGroups
{
[JsonPropertyName("contract")]
public Dictionary<string, string>? Contract { get; set; }
[JsonPropertyName("delivery")]
public Dictionary<string, string>? Delivery { get; set; }
[JsonPropertyName("device")]
public Dictionary<string, string>? Device { get; set; }
[JsonPropertyName("location")]
public Dictionary<string, string>? Location { get; set; }
[JsonPropertyName("system")]
public Dictionary<string, string>? System { get; set; }
}
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger;
public class Resources
{
[JsonPropertyName("language")]
public string? Language { get; set; }
[JsonPropertyName("groups")]
public ResourceGroups? Groups { get; set; }
}
@@ -0,0 +1,10 @@
using Centron.Api.docuFORM.Models.Swagger;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger.Responses;
public class ClientsResponse
{
[JsonPropertyName("clients")]
public List<MpsClient>? Clients { get; set; }
}
@@ -0,0 +1,10 @@
using Centron.Api.docuFORM.Models.Swagger;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger.Responses;
public class CustomerResponse
{
[JsonPropertyName("customer")]
public Customer? Customer { get; set; }
}
@@ -0,0 +1,13 @@
using Centron.Api.docuFORM.Models.Swagger;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger.Responses;
public class CustomersResponse
{
[JsonPropertyName("customers")]
public List<Customer>? Customers { get; set; }
[JsonPropertyName("nextPageToken")]
public string? NextPageToken { get; set; }
}
@@ -0,0 +1,10 @@
using Centron.Api.docuFORM.Models.Swagger;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger.Responses;
public class DealerResponse
{
[JsonPropertyName("dealer")]
public Dealer? Dealer { get; set; }
}
@@ -0,0 +1,10 @@
using Centron.Api.docuFORM.Models.Swagger;
using System.Text.Json.Serialization;
namespace Centron.Api.docuFORM.Models.Swagger.Responses;
public class DealersResponse
{
[JsonPropertyName("dealers")]
public List<Dealer>? Dealers { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More