Files
Masterarbeit/.claude/skills/run-experiment/SKILL.md
T

805 lines
54 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.