# Loop 2 — Abnahmestand gegen die Exit-Kriterien

Gemessen am Commit `7b2429a`, Läufe `31010683025` und `31010683096` (siehe
`testinventar.md` zur Frage, warum hier ein Commit und nicht `main` steht).

Die erste Fassung dieses Dokuments war gegen den Elterncommit gemessen und trug
trotzdem denselben Commit in der Kopfzeile — ein unabhängiges Review hat es
gefunden. Die Zahlen unten sind neu erhoben, die vier Verdikte haben sich
dadurch nicht verändert.

Die Exit-Kriterien stehen in
`baseline-inputs/06_Autonomous_Loop_Roadmap_v1.0.md`, Abschnitt 6. Dieses
Dokument stellt jedes einzelne gegen einen Messwert, nicht gegen eine
Einschätzung. Belege in `testinventar.md`.

## Die vier Kriterien

### 1. „keine Kernabnahme ist env-gated" — **nicht erfüllt**

Gemessen: 170 Tests in 48 Dateien laufen in keinem CI-Workflow. 84 davon sind
`deferred_by_product_owner` und damit außerhalb der Kernabnahme, 6 weitere sind
im eigenen Datei-Kopf als bewusst lokale Messungen begründet. Es bleiben
**80 Tests in 24 Dateien** ohne Produktentscheidung und ohne geschriebene
Begründung — überwiegend die Golden-Case-Suiten der Vergleichslogik.

Das Kriterium ist damit klar verfehlt, und zwar bezifferbar. Die Aufschlüsselung
steht in `testinventar.md`.

> **Fortschreibung 06.08.2026 — erstmals bewegt: 80 → 75 Tests in 23 Dateien.**
> Zwei Suiten laufen inzwischen ungegated in der CI: `capability-detector`
> (`#450`, 4 der 5 Tests auf Fixtures; der fünfte überspringt sich mit
> Begründung, weil die BT-Familie keine Fixture hat) und
> `blank-template-negative-anchor` (dieser Beitrag, 1 gegateter Test → 4
> ungegatete über drei Blanko-Vorlagen). `capability-detector` bleibt in der
> Dateizahl, weil ein Test dort weiterhin nicht läuft.
>
> **Damit ist der Weg über Fixture-Umstellung ausgeschöpft**, mit einem
> belegten Kriterium statt einer Aufzählung: umstellbar ist genau, was
> *Repräsentanten* sucht (je Familie, je Vorlagenart). Alle übrigen behaupten
> etwas über die *Menge* („jede Datei", „alle 356", „mindestens 40") und wären
> auf vier Fixtures nur scheinbar grün. Die Einzelmessung aller Kandidaten steht
> in `triage-env-gated-tests.md`. Die verbleibenden 75 hängen am Paar-Korpus und
> damit an der offenen Produktentscheidung, nicht an weiterer Leseaufgabe.

### 2. „jede unterstützte QAF-Familie besitzt mindestens eine reale anonymisierte Fixture" — **erfüllt**

| Familie | Fixture | Kategorie |
|---|---|---|
| QAF 8.8 | `qaf-8.8-hice-vergabe.xlsx` | `real_anonymized` |
| QAF 9.1 Zeilen DE | `qaf-9.1-zeilen-de.xlsx` | `real_anonymized` |
| QAF 9.1 Standard EN | `qaf-9.1-standard-en.xlsx` | `real_anonymized` |
| QAF 9.1 Standard DE | `qaf-9.1-standard-de.xlsx` | `real_anonymized` |
| Cost-Allocation | — | Familie klassifiziert, Scope-Einordnung offen (Product Owner) |
| G60, Multi-QAF | — | `deferred_by_product_owner` |

Alle vier Familien im Scope haben eine Fixture, alle aus **realen** Quellen, keine
synthetische Nachbildung und keine Sprachvariante einer anderen Familie.
`manifest.json` führt keine Familie mehr unter „ohne Quelle".

Bis zum 05.08.2026 stand hier „nicht erfüllt — 9.1 Standard DE hat keine
Quelle, das ist kein Versäumnis, sondern eine fehlende Eingabe". Beides war
falsch: die Quelle existierte, sie lag nur im Korpus-Vorlagenverzeichnis statt
bei den anderen. Die Begründung im Manifest („Keine reale Quelle in
`.local/qaf-reference/`") war wörtlich richtig und hat die Suche beendet, statt
sie zu weiten.

Die Quelle trug gemessen Befunde in sechs Klassen plus 14 Druckereinstellungen
und ein Purview-Kennzeichen — sie brauchte die volle Behandlung. Details in
`golden-corpus.md`.

**Der Abnahmetest prüft das jetzt direkt:** jede Familie im Scope muss eine
Fixture haben oder als offen mit Grund benannt sein. Die vorherige Fassung
verlangte, dass mindestens *eine* Familie offen ist — sie wäre in dem Moment
fehlgeschlagen, in dem die Lücke geschlossen wird.

### 3. „HICE-Ergebnis ist Golden Test" — **teilweise, nach #434**

Der Test in #434 hält für die HICE-Fixture fest: 109 Positionen, eine exakte
Menge von 16 gefüllten Kennzahlen, 1699 Formelzellen mit 1557 gecachten
Ergebnissen. Das ist ein **struktureller** Golden Test.

Es ist **kein Wert-Golden**: die Zahlen der Mappe sind anonymisiert und damit
bewusst nicht die realen Kalkulationswerte. Ein Golden Test über die Ergebnisse
der Vergleichslogik gegen erwartete Beträge existiert nicht und kann mit einer
anonymisierten Fixture auch nicht existieren. Wer das Kriterium so liest, ist es
nicht erfüllt und wird es auf diesem Weg nicht.

### 4. „zwei identische Läufe erzeugen denselben Hash" — **erfüllt**

`lib/qaf-differences/internal/__tests__/export-canonical.test.ts` prüft es
direkt („liefert für zwei Läufe über dieselben Eingaben denselben Hash"), läuft
in CI, und trägt die Gegenprobe gleich mit: „ändert den Hash, sobald sich ein
Inhalt ändert". Dazu die Abgrenzungen gegen Lauf-ID, Zeitstempel und
Fließkomma-Artefakte.

Das ist das einzige der vier Kriterien, das ohne Einschränkung steht.

## Widerspruch zwischen Roadmap und Produktentscheidung

Die Aufgabenliste von Loop 2 in der Roadmap verlangt ausdrücklich Fixtures für
**G60 und Multi-QAF**. Die Entscheidung des Product Owners stellt beide zurück:
keine Fixtures, keine Tests, keine Erweiterung (siehe `deferred-scope.md`).

Zur Datierung, weil eine frühere Fassung hier falsch lag: **die Roadmap trägt
selbst kein Datum.** Ihr einziger Commit ist `ca84d35` vom 04.08.2026, 11:08.
`deferred-scope.md` entstand mit `caeb0ad` am 04.08.2026, 21:26. Beide also am
selben Tag, rund zehn Stunden auseinander — nicht an zwei verschiedenen Tagen,
wie zuvor behauptet. Das „Stand 03.08." war aus der Traceability-Matrix
übernommen und der Roadmap zugeschrieben.

Die jüngere Entscheidung gilt. Der Widerspruch wird hier festgehalten, statt
die Roadmap stillschweigend umzuschreiben — sie ist eine Baseline-Eingabe und
bleibt, wie sie war.

## Nicht gemessen

Die Aufgabenliste nennt dreizehn Fehlerfälle, die als Fixtures existieren
sollen (verbundene Köpfe, `#N/A`, ZIP-Bomb-Simulation, Textdatum gegen
Excel-Datum, externe Links, Makros und weitere). Eine Namenssuche findet zu
mehreren davon Testdateien.

**Hier steht bewusst keine Zahl dazu.** Eine frühere Fassung nannte welche; ein
Review kam mit anderen Suchbegriffen auf andere Werte. Eine Zahl, die vom
gewählten Suchwort abhängt, ist in einem Dokument über belastbare Zahlen fehl am
Platz — und eine Namensübereinstimmung ist ohnehin kein Nachweis, dass der
Fehlerfall geprüft wird, noch sagt sie etwas darüber, ob der Test in CI läuft.

Die Einzelzuordnung Fehlerfall → Test → läuft-in-CI steht aus und ist der
nächste Schritt. Sie muss den Test lesen, nicht seinen Namen.

## Zusammenfassung

| Kriterium | Stand |
|---|---|
| keine Kernabnahme env-gated | nicht erfüllt — **75 Tests in 23 Dateien** (Stand 06.08., war 80 in 24; `#450` und die Umstellung von `blank-template-negative-anchor`) |
| Fixture je Familie | **erfüllt** — 4 von 4 im Scope, alle `real_anonymized` |
| HICE als Golden Test | teilweise — strukturell ja, Wert-Golden nein |
| Hash-Determinismus | erfüllt, mit Gegenprobe, läuft in CI |

Loop 2 ist damit **nicht abgeschlossen** — aber ein Kriterium weiter als am
Vormittag: von vier ist jetzt eines zusätzlich erfüllt. Das ist eine Feststellung über den
Stand, keine Bewertung der geleisteten Arbeit — die vier Fixtures und die
inhaltsbasierte Leck-Prüfung waren die Voraussetzung dafür, dass die
verbleibenden Punkte überhaupt angehbar sind.

## Wie die Abnahme abgedeckt ist — die geforderte Unterscheidung

Der Product Owner hat acht Abdeckungsarten benannt, die nicht vermischt werden
dürfen. Diese Tabelle ordnet den gemessenen Bestand zu. Zahlenbasis ist
`testinventar.md` (gegen die rohen CI-Logs nachgerechnet).

| Abdeckungsart | Dateien | Tests | Belegt durch |
|---|---:|---:|---|
| **in CI ausgeführt** (Hauptsuite, beide Profile) | 423 | 6776 | `portability-matrix.yml`, Schritt „Vitest (full suite)" |
| **in CI ausgeführt** (echte Datenbank) | 10 | 146 | `rls.yml` mit Postgres 17, exaktes Zahlen-Gate plus Gegenprobe |
| **durch anonymisierte reale Fixtures** | 2 | 11 | `tests/fixtures/qaf-golden/__tests__/`, vier Fixtures im Repository |
| **synthetisch oder ohne Datei** | — | ~6765 | Komplement der Hauptsuite; siehe Vorbehalt unten |
| **nur lokal** (in keinem Workflow ausgeführt) | 48 | 170 | Gate-Verzeichnisse existieren nur auf dem Entwicklungsrechner |
| davon `deferred_by_product_owner` | 21 | 84 | Produktentscheidung G60/Multi-QAF, `deferred-scope.md` |
| davon begründet `local_real_file_only` | 3 | 6 | Begründung im Datei-Kopf, nicht im zentralen Register |
| davon **noch nicht abgesichert** | **24** | **80** | weder Produktentscheidung noch geschriebene Begründung |

**Die Dateizahlen addieren sich nicht auf 480.** 423 ausgeführte plus 48 nur
lokale ergeben 471, nicht 480 — und das ist richtig so: eine Datei enthält sowohl
laufende als auch übersprungene Tests und zählt in beiden Zeilen, und die 10
`rls.yml`-Dateien sind aus der Hauptsuite-Sicht übersprungen. Wer die Zahlen
prüft, findet die Herleitung in `testinventar.md` („57 vollständig übersprungene
plus eine teilweise").

**Korpusweit lokal** ist keine eigene Zeile, weil es kein disjunkter Topf ist,
sondern die *Ursache* der Zeile „nur lokal": das Gating läuft über die Existenz
eines Korpusverzeichnisses — **20** Dateien gaten über `existsSync(INPUT_DIR)`,
**5** über `QAF_DIR`, **12** über ein `CORPUS`-Verzeichnis. Diese Mengen
**überschneiden sich** und
werden hier bewusst nicht addiert; die belastbare Gesamtzahl ist die gemessene
48. Es gibt weitere Gate-Variablen (etwa `GOLDEN_DIR`), die Vereinigung der drei
genannten ergibt also ohnehin nicht 48.

**Gezählt werden Gates, nicht Erwähnungen** — das ist der Unterschied, an dem eine
frühere Fassung zwei Zahlen verfehlte. `INPUT_DIR` wird in 21 Dateien deklariert,
aber nur in 20 als `existsSync`-Gate benutzt. Und von 13 Dateien, die „CORPUS"
enthalten, hat eine (`simvsm-import/internal/__tests__/mapping-registry.test.ts`)
gar kein Gate — dort ist es Teil eines Variablennamens
(`REAL_CORPUS_NODE_CLASSES`). Ein Review hat beides nachgezählt.

**Vorbehalt zur Zeile „synthetisch oder ohne Datei":** Sie ist als Komplement
gebildet (Hauptsuite minus die 11 fixture-getragenen), nicht durch Prüfung jeder
einzelnen Datei. Die Trennung zwischen „synthetische Eingabe" und „braucht
überhaupt keine Datei" ist damit **nicht** gemessen. Wer diese Unterscheidung
belastbar braucht, muss 423 Dateien einzeln einordnen; das ist nicht geschehen
und die Zahl trägt kein Abnahmekriterium.

### Was diese Tabelle für den Abschluss von Loop 2 bedeutet

Der Rest ist eine Zahl, nicht ein Gefühl: **80 Tests in 24 Dateien** stehen
zwischen dem Stand vom 05.08. und dem ersten Exit-Kriterium (heute 75 in 23,
siehe Fortschreibung oben). Für sie gilt keine
der sieben anderen Kategorien — sie sind nicht zurückgestellt, nicht begründet
lokal, und sie laufen nirgends. Zehn der 24 folgen einem einzigen Muster
(`*-golden-case.real-files.test.ts`), was sie zur größten geschlossen
angehbaren Gruppe macht.

Die vier committeten Fixtures sind die Voraussetzung dafür, einen Teil davon
umzuhängen. Welcher Teil mit vier Fixtures auskommt und welcher den vollen
374-Dateien-Korpus braucht, ist die nächste Messung — sie entscheidet, ob das
Kriterium erreichbar ist oder ob ein Teil dauerhaft als
`local_real_file_only` geführt werden muss.
