# Loop 2C: acht PRs, und die wertvollsten waren ein Nein

## Metadaten

- Datum: 2026-08-06 (Nacht vom 05. auf den 06.)
- Ausgangsstand: `3450664` · Endstand: `8c81bac`
- Acht PRs, alle gemergt und einzeln auf `main` verifiziert:
  `#440` `#441` `#442` `#443` `#444` `#445` `#446` `#447`
- Vorgänger: `loop-2b-2026-08-05.md`

## Was jetzt auf `main` steht

| PR | Ergebnis |
|---|---|
| `#440` | Punkt 3 des Handovers widerlegt, bevor der Umbau begann |
| `#441` | 9.1 Standard DE als vierte reale Fixture — Exit-Kriterium 2 erfüllt |
| `#442` | Programmstatus über alle 16 Loops, AWS-Readiness als führende Liste |
| `#443` | Portabilitäts-Gate für den fachlichen QAF-Kern, in beiden CI-Profilen |
| `#444` | Traceability-Zuordnung gemacht, achtstufige Abdeckungsklassifikation |
| `#445` | Machbarkeitsbefund `real_pair_reference_corpus` — vier Blocker |
| `#446` | Manifest sagt, dass die Fixture-Arithmetik nicht stimmt |
| `#447` | Dritte Prüfklasse — HICE war die einzige ungeprüfte Fixture |

## Loop-2-Exit-Kriterien

| Kriterium | Stand |
|---|---|
| keine Kernabnahme env-gated | **nicht erfüllt** — 80 Tests in 24 Dateien |
| 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 |

Die Punkte D bis H sind abgearbeitet: D (`#435`), E (`#432`), F (`#444`),
G (`#440`/`#441`), H ist dieses Dokument.

**Zur Herkunft dieser Buchstaben, weil sie im Repository nirgends steht:** Sie
stammen aus der laufenden Auftragsliste des Product Owners, nicht aus einem
Dokument hier. Die „Nächster Loop"-Liste in `loop-2b-2026-08-05.md` ist
nummeriert, nicht lettiert, und `#432`/`#435` wurden bereits vor dem
2B-Bericht gemergt — „Restliste von Loop 2B" trifft es also nur ungefähr. Wer
diese Vollständigkeit nachprüfen will, braucht die Auftragsliste; aus dem
Repository allein ist sie nicht belegbar. Das gehört gesagt, statt eine
Abschlussmeldung zu machen, die niemand kontrollieren kann.

**Zu Punkt D eine Warnung an den nächsten Durchgang:** Er stand in der
Auftragsliste noch als offen und war seit `#435` erledigt. Beinahe doppelt
gebaut; gefunden hat es der Blick in den Dateikopf, nicht die Liste.
Auftragslisten altern, das Repository nicht.

## Der wichtigste Befund: was ein Nein wert ist

Zwei der acht PRs enden mit einer Absage, und sie sind die nützlichsten.

**`#445` hat einen Bauplan gestoppt.** „Reales Mappenpaar anonymisieren,
Referenz daraus erzeugen, Suiten umhängen" trägt nicht — vier unabhängige
Blocker:

1. Der Rechenschritt für arithmetisch konsistente Mappen **existiert nicht**.
   `--caches=recompute` bricht ab („neu gerechnet wird bisher nirgends"), eine
   Formel-Auswertung gibt es im Projekt nicht. Das ist Neubau.
2. Der Anonymisierer ist **für Paare nicht gebaut**: `replace_number` hasht
   `seed|sheet|coord`, also positionsbasiert. Derselbe Wert an verschiedenen
   Stellen wird verschieden ersetzt, verschiedene Werte an gleicher Stelle
   gleich. Die Textersetzung nutzt einen Zähler je Datei und liest den Seed
   nicht. Ein gemeinsamer Seed hilft **nicht**.
3. Die Referenz ist **nicht codegeneriert** — teils „forensisch von Hand"
   erstellt; `buildV2Export()` hat null Produktionsaufrufer und eine andere Form.
4. Ein Wert-Golden auf anonymisierten Mappen ist laut `loop-2-abnahmestand.md`
   ohnehin nicht möglich — eine Feststellung dieses Programms, die der erste
   Bauplan übergangen hatte.

**`#446` hat eine stille Falschaussage beseitigt.** Vier eingecheckte Fixtures
sind arithmetisch inkonsistent, und das Manifest verschwieg es. Wer Beträge
gegen sie geprüft hätte, hätte zwei getrennt gewürfelte Zahlen verglichen.

## Die Fehlerklasse dieser Nacht

Fast jeder Review-Blocker hatte dieselbe Ursache: **geprüft wurde, was ein
Werkzeug leicht zeigt, statt was die Behauptung verlangt.**

- Vier übereinstimmende Feldnamen als Beweis für Strukturgleichheit genommen —
  die reale Referenz ist flach, `buildV2Export()` verschachtelt unter `areas`.
- Zweimal eine Substring-Trefferliste gezählt statt gelesen. Einmal war der
  Treffer ein Kommentar, der ausdrücklich das Gegenteil festhielt; einmal der
  Standardwert eines Seed-Arguments.
- Eine Laufzeitangabe als Beleg geführt, deren einziger Fundort im Repository
  der eigene Satz war.
- Eine „Regressionssperre" gebaut, die zwei Felder verglich, die aus **derselben
  Zeile** entstehen. Die Tautologie-Falle war einen Tag zuvor im eigenen
  Machbarkeitsbefund beschrieben.

**Der schwerste Fall dieser Nacht fehlte in der ersten Fassung dieses
Abschnitts, und das war selbst ein Beispiel für die Klasse.** In `#441` fand ein
Review reale Mandanten-Kennungen aus SharePoint (`ma:contentTypeID`,
`ma:versionID`, `ma:fieldsID`) in **allen vier** Fixtures — drei davon lagen
bereits auf `main`, seit Tagen, trotz mehrfacher Leck-Meldungen mit dem
Ergebnis „keine Funde". Ursache war eine Namensliste, die die Umstellung auf
inhaltsbasierte Erkennung überlebt hatte.

Die erste Fassung nannte vier epistemische Fehler und ließ diesen aus. Das ist
die unangenehmere Sorte Auslassung: nicht verschwiegen, sondern **kuratiert** —
die abstrakten Fehler benannt, den konkreten Beinahe-Datenvorfall nicht. Ein
Review hat es gefunden. Bemerkenswert außerdem: der Vorfall ist bis heute in
keinem anderen Dokument festgehalten, weder in `golden-corpus.md` noch im
2B-Bericht. Dieser Absatz ist die einzige Stelle.

Was hilft, ist nicht das Wissen um die Klasse, sondern eine konkrete Frage vor
jeder Prüfung: **woher kommt die Vergleichsseite?** Zwei verschiedene Feldnamen
sind kein Beweis für zwei Quellen.

Der Ersatz in `#446`/`#447` zeigt, wie es geht: nicht die Zusage neben sich
selbst legen, sondern das Artefakt messen. Eine reine Zellreferenz muss den Wert
ihrer Zielzelle tragen; eine `IF(…,0,1)`-Formel kann nur 0 oder 1 ergeben; eine
`SUM` über reine Zahlenliterale ist nachrechenbar. 5434 von 7410 Caches
widersprechen dem nachweislich.

## Was ein Fehlalarm anrichtet

`#447` schließt einen Fehlalarm auf **korrekten** Dateien: Bei verbundenen Zellen
spiegelt ExcelJS den Master-Wert auf jede Slave-Zelle, die Summe fällt zu hoch
aus. Im heutigen Korpus tritt es nicht auf — aber ein Test, der auf richtigen
Daten anschlägt, wird abgeschaltet, lange bevor jemand die Ursache sucht. Das
ist die gefährlichere Richtung als ein übersehener Fehler.

## Offene Punkte

**Für den Product Owner** (`#445`, Abschnitt 6): ob ein Wert-Golden auf
anonymisierten Mappen das Ziel ist. Vier Alternativen stehen dort; eine —
rein strukturelle Prüfungen nach dem Muster der vier Einzelfixtures — braucht
keinen der vier Blocker, bedient aber nur einen Teil der 48 referenzgebundenen
Tests.

**Blocker, unverändert:**

- `deployment_only_blocker`: Migration `#117` auf Produktion (Runbook liegt)
- `external_validation_pending`: `production_schema_dump_validation_pending`
- `loop_local_blocker`: `ANONYMISE_SEED` fehlt — **nicht** der begrenzende
  Faktor, siehe Blocker 2 oben

**Widerspruch, der vor dem nächsten Loop weg muss:** `program-status.md` führt
`real_pair_reference_corpus` weiterhin mit „Der Weg ist ausdrücklich freigegeben
— autonom zu beginnen, ohne erneute Rückfrage" und mit „54 Tests in 10 Dateien".
Beides ist durch `#445` aus derselben Nacht widerlegt: der Weg trägt nicht
(vier Blocker), und es sind 48 Tests in neun Suiten. Ein Nachfolger, der nur die
zentrale Übersicht liest, bekommt eine überholte Freigabe zum Losbauen — genau
das, was dieses Dokument verhindern soll. Ein Review hat es gefunden.

**Behoben in diesem Beitrag:** `program-status.md` nennt jetzt 48 Tests in neun
Suiten, nimmt die Freigabe ausdrücklich zurück und verweist auf den Befund. Eine
Notiz im Handover hätte den Widerspruch nur beschrieben; er stand aber in der
Datei, die ein Nachfolger zuerst liest.

**Technisch, ohne Entscheidung angehbar:**

- Die 200er-Größengrenze in der Summenprüfung überspringt reale Bereiche
  (`SUM(BD16:BD3015)`). Bewusste Kostengrenze, benannt, nicht gemessen.
- Die Prüfung deckt rund 1,7 % aller Formeln ab. `arithmetic_consistency` ist
  damit ein schwaches Signal über den Gesamtbestand — belastbar für die Frage
  „ist die Zusage widerlegt", nicht für „stimmt alles Übrige".
- Die 80 unentschiedenen Tests in 24 Dateien bleiben das erste Exit-Kriterium.

## Verfahren, das getragen hat

Jeder PR ging durch ein unabhängiges Review vor dem Merge, mehrere durch drei
oder vier Runden. **Die schweren Befunde kamen fast alle aus den Reviews** — mit
einer Ausnahme, die hier stehen muss, weil eine zu bescheidene Aussage genauso
falsch wäre wie eine zu großzügige: Die arithmetische Inkonsistenz der Fixtures
(`#446`) habe ich selbst gefunden, beim Messen für den Machbarkeitsbefund;
`#445` führt sie dort als „ein eigener offener Punkt". Der Bericht zählt sie zu
den zwei nützlichsten Ergebnissen der Nacht.

Alles andere kam von außen. Die Reviews haben nicht nur gelesen, sondern
angegriffen: Umgehungen als echte
Dateien gebaut, Mutationen mit Anker-Verifikation gefahren, Zahlen mit eigenen
Skripten nachgerechnet statt die Behauptung zu prüfen.

Was daraus als Regel bleibt: Ein Review, das nur den Diff liest, findet diese
Klasse nicht. Es muss die Behauptung des PR gegen das Artefakt halten dürfen —
und dafür braucht es den Auftrag, genau das zu tun.
