Dokumentation: Seiten auswählen

Altlasten: Erneuerung und Inhaltsreview

Vertrag: ADR-0058 (Ergänzungen #556, #582, #602 und #640); Betrieb als Vercel-Cron seit 06.10.2026: ADR-0080. Wiederherstellung #556.

Auf einen Blick (seit ADR-0080)

WasWo
NachtlaufVercel-Cron /api/cron/renew-altlasten-snapshot, täglich 03:37 UTC (Funktionslimit 800 s, WFS-Deadline 600 s, 120 s je Abruf; der Skriptschritt brauchte auf GitHub Actions 40–73 s, Stichprobe 16.09.–06.10.2026)
Ergebnis eines Laufs/admin/cron/renew-altlasten-snapshot/runs/<id> (vollständiges Ergebnis-JSON ohne Rohgeometrie, cron_runs.result_json)
ReviewpflichtMail an office@ostheimer.at: „[LandNutzen] Altlasten: Inhaltsreview erforderlich …“ mit Frist, Diff, fertigem Aktivierungsbefehl und Anhängen (Ergebnis-JSON, Quellinventar); dieselbe Anleitung im Admin-Lauf
Reviewpflicht entfallenMail „[LandNutzen] Altlasten: Reviewpflicht entfallen“
Aktivierungweiterhin Handarbeit: manueller Workflow publish-altlasten-release.yml oder lokales CLI (siehe „Kontrollierte Aktivierung“)
Manueller Lauf/admin/cron → Zeile renew-altlasten-snapshot → „Jetzt triggern“, oder curl mit CRON_SECRET (siehe unten)

Der GitHub-Workflow renew-altlasten-snapshot.yml hat seit #669 keinen Zeitplan mehr (zuletzt Rückfall 06:37 UTC, ursprünglich 03:37 UTC). workflow_dispatch und der geprüfte Codepfad bleiben erhalten. Der Workflow ist gemäß #685 in GitHub deaktiviert (disabled_manually, Readback 13.10.2026); dieser Zustand verhindert auch einen manuellen Start. Ein Notlauf über Actions braucht eine gesondert freigegebene Wiederaktivierung und verbraucht Actions-Minuten. Der Vercel-Cron bleibt der reguläre Betriebsweg.

Erster belegter Vercel-Lauf: 07.10.2026, 03:37 UTC. Die öffentliche Provenance nennt für den aktiven Release retrievedAt 03:37:22 und activatedAt 03:37:23 UTC (Pointer-Version 21). Der Rückfall-Workflow startete am selben Tag um 06:59 UTC und übersprang mit active_snapshot_has_freshness_reserve. Das ist der Zeitpunkt des Vercel-Zeitplans; ob vercel oder manual ausgelöst hat, zeigt die Lauf-Zeile unter /admin/cron. Den Nachweis für Hochwasser und Brachflächen führen deren Runbooks.

Aktueller Betriebsbeleg 13.10.2026: Vercel-Lauf #2944 startete um 03:37:07 UTC und endete mit success (15.088 ms). Er erneuerte Release aadac1bb-49b4-567f-a586-b650227069ea, Pointer 27, 383 veröffentlichte Geometrien, frisch bis 15.10.2026 03:37:21 UTC. Der technische Abruf ist kein neuer fachlicher Quellenstand (sourceVintage=null). Öffentliche Provenienz und Coverage wurden lesend gegengeprüft. Betriebsbeleg #685.

Jeder WFS-Abruf darf seit 06.10.2026 bis zu 120 s dauern (vorher 60 s): Die erste Seite brauchte abends 63–71 s bis zum ersten Byte, die übrigen 1–4 s. Die Gesamtdeadline begrenzt weiterhin alles (Vercel-Route 600 s, Skript im GitHub-Workflow 15 min).

Manueller Lauf mit dem Produktions-Secret, ohne es anzuzeigen:

read -rs CRON_SECRET && export CRON_SECRET
curl -sS --max-time 900 -X POST \
  -H "Authorization: Bearer $CRON_SECRET" \
  -H "X-Cron-Triggered-By: manual" \
  https://www.landnutzen.at/api/cron/renew-altlasten-snapshot

200 mit result = erneuert oder belegter Skip. 500 mit ALTLASTEN_CONTENT_CHANGE_REVIEW_REQUIRED:<kandidat> = Reviewpflicht (Mail folgt). Ein manueller Lauf ist ein normaler Lauf und schreibt wie der Nachtlauf in die Produktionsdatenbank.

Aktuellen Zustand lesen

Maßgeblich ist die öffentliche Provenance, insbesondere activeRelease.id, activationRef.pointerVersion und freshness.state/freshUntil. Der Abrufzeitpunkt ist technisch, kein fachlicher Datenstand. Ein historischer Produktionsnachweis garantiert keine heutige Frische. Der Report zeigt bei gesperrter Quelle „Altlasten-Prüfung für diese Adresse nicht verfügbar.“ Das ist kein Nullbefund. Persistierte Reports behalten ihren damaligen Zustand.

Täglicher Betrieb und rechtzeitige Warnung

Der Vercel-Cron renew-altlasten-snapshot läuft täglich um 03:37 UTC (bis 06.10.2026 der GitHub-Workflow renew-altlasten-snapshot.yml, dessen Start GitHub um 6 bis 166 Minuten verzögerte, Stichprobe 28.08.–06.10.2026, ADR-0080). Drei vollständig identische Abrufe sind Pflicht. Die Frist beträgt unverändert höchstens 48 Stunden, die Erneuerungsreserve seit #582 36 Stunden (zuvor 24 Stunden — bei Reserve gleich Cron-Takt lief jede Erneuerung auf die Frist zu; belegt waren fünf Erneuerungen mit unter drei Minuten Rest). Größer als der tägliche Takt zu sein heißt: nach jeder Erneuerung (Rest 48 h) liegt der Rest in der folgenden Nacht (~24 h) unter der Reserve und es wird wieder erneuert — jede Nacht, nie mehr auf das Fristende hin. Der unabhängige Admin-/Drift-„due“- Schwellwert (lib/data-sources/drift-policy.ts) bleibt eine eigene, unveränderte Konstante und wird davon nicht berührt.

„Inhaltsgleich“ heißt seit #582 gleiche publishedManifestSha256, excludedManifestSha256 und scopeManifestSha256 sowie gleiche Transform- und Scope-Version — nicht mehr das Gesamt- oder das Quellmanifest der amtlichen Quelle, das rund 1.434 Sätze außerhalb des Altlast-Scopes einschließt. 7 von 10 Quelländerungen im September lagen ausschließlich außerhalb des Scopes; solche Änderungen erneuern jetzt automatisch und bleiben nur noch informativ als Außer-Scope-Drift im Delta sichtbar. Nur ein in diesem Sinn vollständig inhalts- und vertragsgleicher Abruf bei offenem Public-Gate darf ohne Feldreview automatisch erneuert werden. Ein bereits abgelaufener Release braucht auch bei gleicher Quelle eine explizite Aktivierung.

Seit #602: nur Ausschluss/Außer-Scope geändert. Am 24./25. September 2026 war die Altlasten-Prüfung rund sechs Stunden nicht verfügbar, obwohl sich am Bericht nichts geändert hatte: neu war nur ein ausgeschlossener Eintrag (UBA-ID 83984, Status „Altlast vorgeschlagen“) plus zwei Sätze außerhalb des Scopes; publishedManifestSha256 und alle 383 veröffentlichten Geometrien waren byte-gleich. Weil sich damit Ausschluss- und Scope-Manifest änderten, war trotzdem ein Review nötig (#602). PM-Entscheidung vom 25. September 2026: Änderungen, die nur ausgeschlossene oder Out-of-Scope-Einträge betreffen, gehen ohne Review durch, solange Veröffentlichtes und alle Geometrien byte-gleich sind.

Der Nachtlauf aktiviert einen Kandidaten mit geändertem Ausschluss- oder Scope-Manifest deshalb nach dem feldweisen Review im selben Lauf automatisch (Grund excluded_or_scope_only_change_renewed, Expected-Current-CAS wie bei der inhaltsgleichen Erneuerung), wenn alle folgenden Punkte gelten — sonst, auch bei jeder fehlenden oder ungültigen Angabe, bleibt es beim Review wie bisher:

  • aktiver Release vorhanden, öffentliches Gate offen;
  • Transform- und Scope-Version bei aktivem Release und Kandidat gleich der laufenden Logik; Kandidat rechtlich zulässig und frisch; Rechte-Review-Digests (Datei und Zustand) unverändert;
  • Feldreview fehlerfrei, für genau diesen aktiven Release (Pointer-Version) und genau diesen Kandidaten;
  • publishedManifestSha256 bei aktivem Release, Snapshot und Kandidat ein gültiger SHA-256 und identisch;
  • veröffentlichte Zeilen ohne neue, entfernte, geänderte oder im Umriss geänderte Flächen;
  • Geometrievergleich räumlich und binär gleich, compared gleich der veröffentlichten Anzahl;
  • Ausschluss- und Scope-Manifest auf beiden Seiten gültig; die feldweise Ausschluss-Delta erklärt die Manifeständerung.

Wandert ein Eintrag aus der Ausschlussmenge in die Veröffentlichung (oder umgekehrt), ändert sich die veröffentlichte Menge — das bleibt ausnahmslos reviewpflichtig. Das Ergebnis enthält bei automatischer Freigabe automaticApproval mit kind: "excluded_or_scope_only", den neuen, entfernten und geänderten Ausschluss-UBA-IDs sowie den Zählern Ausschluss/Außer-Scope vorher → nachher; bei Reviewpflicht stehen die Sperrgründe in automaticApprovalBlockers (etwa published_rows_changed oder published_geometry_changed). Das Ergebnis-JSON des Laufs im Admin nennt die automatische Freigabe mit denselben IDs, geometriefrei. War der vorige Lauf noch reviewpflichtig, geht eine Entwarnungsmail mit Art und IDs raus (bis 06.10.2026: Schließkommentar des Review-Issues); für eine automatische Freigabe allein wird nichts gemailt. Nachweis ist das Ergebnis-JSON des Laufs (/admin/cron/renew-altlasten-snapshot/runs/<id>, 90 Tage Aufbewahrung wie zuvor das Actions-Artefakt).

Bewusster Trade-off: Neue Ausschlüsse werden nicht mehr einzeln bewusst gesichtet. Da ausgeschlossene Einträge nie im Bericht erscheinen, zeigt der veröffentlichte Bericht dadurch nie mehr als vorher; übersehen werden kann nur, dass ein Vorschlag auftaucht oder verschwindet.

Seit #640: nur Geometrie-Rauschen an veröffentlichten Flächen. Am 3. und 4. Oktober 2026 meldete der Nachtlauf drei geänderte veröffentlichte Flächen (#640). Zwei davon — O19 „Spenglerei Aumayr“ (UBA-ID 72284) und O91 „Chemische Reinigung Wozabal Katzbach“ (81730) — hatten nur einen neuen Umriss: größte Grenzverschiebung je rund 5,8 mm, hinzugekommene plus weggefallene Fläche 0,19 m² bzw. 0,02 m², kein Feld geändert. Die dritte, N86 „Teerablagerungen Kromag“, hatte echte Feldänderungen (sanArt und sanStatus von leer auf „Beobachtung“ bzw. „in Planung“). Freigabe von Andreas am 6. Oktober 2026: Reines Geometrie-Rauschen geht ohne Review durch.

Der Nachtlauf aktiviert einen Kandidaten mit geändertem veröffentlichtem Manifest deshalb nach dem feldweisen Review automatisch (Grund published_geometry_noise_renewed, Expected-Current-CAS), wenn alle folgenden Punkte gelten — sonst, auch bei jeder fehlenden oder ungültigen Angabe, bleibt es beim Review:

  • dieselben Vorbedingungen wie bei #602: aktiver Release, offenes Gate, gleiche Transform- und Scope-Version, Kandidat zulässig und frisch, Rechte-Review-Digests unverändert, Feldreview fehlerfrei für genau diesen aktiven Release und Kandidaten;
  • keine neue und keine entfernte veröffentlichte Fläche, gleiche Anzahl auf allen Seiten;
  • jede geänderte Fläche hat kein einziges geändertes Feld, nur einen neuen Geometrie-Hash;
  • für jede dieser Flächen liegen vollständige Kennzahlen vor (höchstens 200 je Lauf), und Fläche nachher minus vorher stimmt mit hinzugekommener minus weggefallener Fläche überein;
  • größte Grenzverschiebung (ST_HausdorffDistance) unter 1 cm;
  • hinzugekommene plus weggefallene Fläche kleiner als ein 1 cm breiter Streifen entlang des kürzestmöglichen Umrisses dieser Fläche (Kreis gleicher Fläche, also 1 cm × 2·√(π·Fläche)) — für O19 sind das 2,57 m², für O91 0,53 m²;
  • Ausschlussmenge unverändert, Scope-Manifest gültig und zum Snapshot passend.

Sobald auch nur ein Feld einer veröffentlichten Fläche geändert, eine Fläche neu oder entfernt ist oder eine Verschiebung die Schwelle erreicht, bleibt der Lauf rot und reviewpflichtig. Ändern sich Ausschlussmenge und Umrisse im selben Lauf, greift keine der beiden Ausnahmen — sie gelten nur einzeln. Der Lauf vom 4. Oktober wäre deshalb auch mit dieser Regel reviewpflichtig gewesen (N86; der aktive Stand war da zudem schon abgelaufen, weil schon der Lauf vom 3. Oktober an N86 hing). Ein Lauf nur mit O19 und O91 wäre am 3. Oktober automatisch durchgegangen.

Das Ergebnis enthält bei automatischer Freigabe automaticApproval mit kind: "published_geometry_noise" und je Fläche UBA-ID, Altlast-Nr., Bezeichnung, Verschiebung, Fläche vorher und nachher, hinzugekommene und weggefallene Fläche sowie die zulässige Grenze. Bei Reviewpflicht stehen die Sperrgründe dieser Regel in geometryNoiseApprovalBlockers (etwa published_fields_changed oder geometry_shift_above_threshold), die der #602-Regel weiterhin in automaticApprovalBlockers. Ergebnis-JSON im Admin und gegebenenfalls die Entwarnungsmail (nur UBA-IDs und Zahlen) nennen IDs und Verschiebungen, ohne Rohgeometrie oder Koordinaten.

Bewusster Trade-off: Millimeterverschiebungen werden nicht mehr einzeln gesichtet. Eine Verschiebung unter 1 cm ändert das Ergebnis nur für einen Adresspunkt, der weniger als 1 cm vom Rand liegt; der Abstand im Bericht ist auf ganze Meter gerundet. Begründung der Schwellen und Restrisiko stehen in ADR-0058, Ergänzung #640.

Jede andere Änderung am veröffentlichten Inhalt bleibt gesperrt: qualifizierter Kandidat, fehlgeschlagener Lauf (failed in /admin/cron), Review-Mail und Admin-Lauf mit aktiver Frist, separater Kandidatenfrist, Public-Gate, Published-/Excluded-/Scope-Delta und — neu seit #582 — dem im selben Lauf sofort ausgeführten feldweisen Review (neu veröffentlichte Flächen mit UBA-ID/Altlast-Nr./Bezeichnung/Status vorher-nachher, Attributänderungen, Entfernungen, Geometrie-Gleichheit), ohne Rohgeometrie, Koordinaten oder Zugangsdaten. Seit #587 zeigt eine Fläche mit geändertem Umriss die Zeile explizit an, auch wenn kein ausgewertetes Attribut betroffen ist — zuvor blieb die Änderungszeile in diesem Fall leer. Bei bis zu 200 gleichzeitig geänderten Umrissen je Lauf nennt die Zeile zusätzlich Fläche vorher/nachher, hinzugekommene und weggefallene Fläche sowie die größte Grenzverschiebung in Metern (ST_Area/ST_HausdorffDistance, EPSG:31287, also direkt in m²/m ohne Umrechnung); darüber hinaus bleibt es beim reinen Hinweis „Umriss geändert“ ohne Zahlen, die Gesamtzahl betroffener Flächen steht weiterhin in der Geometriezeile. Seit #640 zeigt die Zeile Werte unter 0,5 mit zwei signifikanten Stellen (zuvor erschienen 5,8 mm als „Grenze bis 0 m verschoben“ und 0,12 m² als „+0 m²“); fehlt eine Kennzahl, bleibt es beim Hinweis „Umriss geändert“ statt einer scheinbaren 0. Eine Flächen- oder Randänderung ist kein inhaltlicher Befund für sich — sie kann eine Adresse neu innerhalb oder in der Nähe eines Altlastenbereichs erscheinen lassen (oder umgekehrt herausfallen lassen), auch wenn sich am Altlastenstatus selbst nichts geändert hat; das gehört in die fachliche Prüfung vor einer Aktivierung. Weiterhin ohne Rohgeometrie oder Koordinaten — nur Flächen- und Distanzzahlen. Bei Reviewpflicht schickt der Lauf seit ADR-0080 eine Mail an office@ostheimer.at mit Frist, Grund, Feldreview, der fertigen Aktivierungsanleitung (beide Wege, mit den konkreten UUIDs) und dem Link auf den Lauf im Admin; Anhänge sind Ergebnis-JSON und Quellinventar. Jeder reviewpflichtige Lauf schickt eine eigene Mail, weil jeder Lauf einen neuen Kandidaten mit neuer UUID erzeugt — maßgeblich ist immer die jüngste. Der Lauf bleibt failed; der allgemeine Cron-Fehleralarm entfällt dann, außer die Review-Mail scheitert (dann alarmiert der Fehlerpfad, und das Inventar bleibt im Ledger dieses Laufs). Entfällt die Reviewpflicht in einem späteren Lauf, folgt eine Entwarnungsmail. Das GitHub-Issue altlasten-review (#582 Baustein C) wird nicht mehr angelegt; der Code dafür bleibt nur für einen manuellen Lauf des GitHub-Workflows aktiv. Der Admin zeigt bei einem neueren erfolgreichen, nicht aktiven Altlastenimport ausdrücklich den offenen Kandidaten. Ein technischer Importerfolg ist keine Inhaltsfreigabe, und nichts im Nachtlauf aktiviert einen Kandidaten mit Änderung am veröffentlichten Inhalt automatisch (Ausnahmen sind nur die engen Regeln oben: #602, bei der Veröffentlichtes und alle Geometrien byte-gleich bleiben, und #640, bei der sich an veröffentlichten Flächen nur Umrisse um weniger als 1 cm verschieben).

Statt der Actions-Artefakte altlasten-renewal-review-<run> (bis 06.10.2026) gilt: Das Ergebnis jedes Laufs steht geometriefrei in cron_runs.result_json (Admin-Lauf, 90 Tage, cron-prune), dazu ein Inventar-Nachweis sourceInventorySummary (Zeilenzahl, Klassifikation, SHA-256). Das vollständige geometriefreie Quellinventar (UBAID, Scopeklassifikation, Zeilen- und Geometriehash, ~0,4 MB) hängt nur an der Review-Mail und bleibt damit im Postfach erhalten. Keine Rohgeometrie, Report-ID oder Zugangsdaten hineinschreiben. Ein Abruffehler vor einem vollständigen Snapshot erzeugt kein scheinbares Inventar.

Der stündliche Driftmonitor zeigt außerdem due ab 24 Stunden vor Ablauf und stale nach Ablauf im Admin. Am 5. September war dessen damalige Mailzustellung wegen einer unverifizierten Resend-Domain fehlgeschlagen. Seit 25.09.2026 versendet der Code Drift-Alarme als Tagesdigest und Cron-Fehler sofort über world4you-SMTP an office@ostheimer.at (ADR-0048, Nachtrag); seit ADR-0080 ist diese Mail auch der Reviewkanal. Als abgenommen gilt sie erst nach dem ersten belegten Empfang einer Review- oder Fehlermail in Produktion. Der überprüfbare Ort bleibt /admin/cron/renew-altlasten-snapshot (roter Lauf mit Ergebnis und Anleitung) — eine Mail, die SMTP annimmt, aber im Spam landet, löst keinen Ersatzalarm aus. Die verantwortliche Projektbetreuung prüft einen roten Lauf vor der genannten aktiven Frist.

Review ohne Mutation

Läuft automatisch im Nachtlauf, sobald ein Kandidat reviewpflichtig wird (mit dem dort bereits geladenen Snapshot, ohne zusätzlichen WFS-Abruf, und mit der Kandidaten-ID explizit übergeben — nicht erraten). Für einen manuellen Lauf mit bestehendem projektbezogenem DB-Zugang in der Prozessumgebung:

node scripts/review-altlasten-snapshot.mjs /privater/pfad/altlasten-review.json

Ohne Kandidaten-ID verhält sich das Script wie bisher: es sucht den neuesten qualifizierten Kandidaten, vergleicht beide Published-/Excluded-Bestände feldweise und liest die amtliche Quelle erneut vollständig dreimal. Der Abbruch bei abweichender Quelle prüft seit #582 nur noch die berichtsrelevanten Manifeste (publishedManifestSha256, excludedManifestSha256, scopeManifestSha256); eine reine Außer-Scope-Änderung der Quelle lässt den Review durchlaufen und erscheint stattdessen als informative Außer-Scope-Drift im Ergebnis. Ein zwischenzeitlich geänderter Pointer stoppt den Review weiterhin. Die Ausgabe enthält keine Rohgeometrien. automaticApproval: false im Review-Ergebnis ist verbindlich: Auch unveränderte Geometrien erlauben keine allgemeine Ausnahme für neue Attributänderungen. Automatische Freigaben trifft nur der Nachtlauf selbst nach den Regeln #602 (nur Ausschluss/Außer-Scope, Veröffentlichtes byte-gleich) und #640 (nur Geometrie-Rauschen unter 1 cm, Felder unverändert); das Review-Script entscheidet nichts.

Vor Aktivierung prüfen: exakter Release/Manifest, aktiver Pointer-CAS, aktuelle Uhrzeit und Frist, Rechte-/Scope-/Transformbindung, neun erforderliche Qualitätsprüfungen, gespeicherte Zeilen gegen den aktuellen WFS und konkreter Einfluss auf den Report. Gleiche Counts genügen nie. Fehlende historische Quellenzeilen ausdrücklich als Evidenzlücke behandeln. Eine neue unbekannte Änderung braucht eine neue Entscheidung.

Kontrollierte Aktivierung

Die Entscheidung dokumentiert Zielrelease, Gesamtmanifest, Expected-Current, Rechteentscheidung, Delta, Gültigkeit und Rückweg. Nach PM-Abnahme:

node scripts/publish-altlasten-release.mjs \
  --activate-release=<geprüfter-release> \
  --expected-current-release=<frisch-gelesener-aktiver-release> \
  --confirmation='ACTIVATE altlasten public'

Alternativ über den Workflow (die exakten workflow_dispatch-Input-Namen aus .github/workflows/publish-altlasten-release.yml; Review-Mail und Admin-Lauf enthalten den fertigen Befehl mit den konkreten UUIDs):

gh workflow run publish-altlasten-release.yml --repo ostheimer/landnutzen \
  -f action=activate \
  -f candidate_release=<geprüfter-release> \
  -f expected_current_release=<frisch-gelesener-aktiver-release> \
  -f typed_confirmation='ACTIVATE altlasten public'

Entscheidung ADR-0080: Der Workflow publish-altlasten-release.yml bleibt ein rein manueller GitHub-Workflow. Er läuft selten (nur nach einem Review) und verbraucht Actions-Minuten nur, wenn er gestartet wird; das lokale CLI oben ist gleichwertig, braucht aber den Produktions-DB-Zugang in der Shell. Ein Umbau auf einen Vercel-Endpunkt würde eine schreibende Aktivierung über HTTP öffnen und ist deshalb nicht vorgesehen.

Kein direktes UPDATE auf Fristen, Rechte oder Pointer. Danach öffentlichen Release/Pointer, neue anonyme öffentliche Referenzreports (Hit/No-Hit) und Web/API/PDF rücklesen. Keine bestehenden persönlichen Reports suchen oder ändern. Den normalen Renewal-Pfad erneut ausführen: Bei ausreichender Reserve ist ein belegter Skip korrekt; ein erzwungener Fristwechsel wäre es nicht. Einen kontrollierten Lauf nie als bereits erfolgten nächsten planmäßigen Lauf ausgeben.

Rollback und Wiederanlauf

Der generische Rollback verlangt einen früher aktivierten und weiterhin frischen und zulässigen Release, aktuellen CAS, Operation-ID und Grund:

node scripts/publish-altlasten-release.mjs \
  --rollback-to=<zulässiger-früherer-release> \
  --expected-current-release=<aktueller-release> \
  --rollback-reason='<konkreter-grund>' \
  --confirmation='ROLLBACK altlasten public'

Der Initialrelease vom 26. August ist seit 28. August stale und damit kein verwendbares Rollbackziel für #556. Ein Code-Rollback auf den vorherigen Main-Commit lässt das unveränderte DB-Schema und den freigegebenen Quellenpointer bestehen. Bei einem Quellenproblem gibt es ohne frischen früheren Release keinen zulässigen Datenrollback: Review stoppen, keine Folgeaktivierung, geschlossene Gates respektieren und nach frischem Drei-Pass-Abruf einen neuen Kandidaten qualifizieren und separat entscheiden. Kein Wiederbeleben alter Daten durch Fristverlängerung. Stale-/Rechte-/Rollback-Sperren werden lokal getestet, nicht absichtlich in Produktion ausgelöst.