Dokumentation: Seiten auswählen

Report-Spezifikation — JSON-Contract

Nahversorgungsdarstellung (#564)

900 m Luftlinie als Näherung für 15 Minuten zu Fuß; keine berechnete Route. Umwege und Barrieren werden nicht berücksichtigt. Radius und Ampelschwellen bleiben gemäß #463 unverändert. Der Ausgabevertrag regelt neue Reports und die rein darstellungsseitige Korrektur bekannter Alttexte.

Dieses Dokument beschreibt den deployten Report-JSON-Contract von LandNutzen. Verbindliche technische Quelle ist lib/report/types.ts; die Assembler- und Tierlogik liegt in lib/report/build.ts und lib/report/tier.ts. Frühere Zielannahmen zu einem separaten Puppeteer-Premium-Template, einem Top-Level-narrative, confidence, bestand oder grundbuch sind nicht Teil des aktuellen Contracts.

Der öffentliche PDF-Pfad rendert alle Tiers mit @react-pdf/renderer und pdf-lib. Der reine Node.js-PDF/UA-Shadow aus ADR-0060 verwendet nur feste synthetische Daten in geschützten Vercel Previews und verändert weder diesen JSON-Contract noch den Produktionsrenderer.

Der optionale Quellenanhang (/api/v1/report/{id}/sources/pdf, #739) verwendet unverändert dieselbe gespeicherte sources-Liste. Die Nummern sind reine Darstellung und ergänzen keine Felder im JSON-Contract. Hauptreport und Anhang bleiben über Report-ID, meta.generated_at und meta.as_of zuordenbar.

Querverweise: PRODUCT.md (nicht im öffentlichen Doku-Portal verfügbar) · ARCHITECTURE.md (nicht im öffentlichen Doku-Portal verfügbar) · ADR-0015 Kostenschätzung (nicht im öffentlichen Doku-Portal verfügbar) · ADR-0013 PDF (nicht im öffentlichen Doku-Portal verfügbar) · ADR-0006 Auth/Multitenant (nicht im öffentlichen Doku-Portal verfügbar) · ADR-0016 Grundbuch (nicht im öffentlichen Doku-Portal verfügbar) · ADR-0014 HORA-Pass (nicht im öffentlichen Doku-Portal verfügbar) · DATA-SOURCES.md · POC-PLAN.md (nicht im öffentlichen Doku-Portal verfügbar)


1. Grundprinzipien

1.1 Trennung Engine ↔ LLM (verbindlich)

Konsistent mit ADR-0015 (nicht im öffentlichen Doku-Portal verfügbar) berechnet die Engine Zahlen, Ampeln, Kosten und Förderzuordnungen. Erst danach darf ein optionaler LLM-Aufruf die Fakten nicht roter Nicht-PV-Szenarien in ein bis zwei Sätze umformulieren. Die LLM-Eingabe filtert rote und PV-Szenarien; die Zuordnung einer formal gültigen Modellantwort prüft die Ampel derzeit jedoch nicht ein zweites Mal. Das Ergebnis liegt ausschließlich in scenarios[].narrativ und bleibt unverbindlicher Zusatztext.

Das Runtime-Gate erlaubt diesen Aufruf nur für standard und premium. free und planer bleiben auch bei gesetztem AI-Gateway-Key ohne LLM-Aufruf. Ohne Key, bei Budgetgrenze, Timeout, Provider- oder Parsefehler bleibt der Report vollständig und das Narrativ fehlt; meta.degraded weist den Grund aus.

Der Prompt verbietet neue Zahlen und Bewertungen. Ein separater Regex-Zahlenabgleich, Auto-Retry oder Top-Level-guard_passed ist derzeit jedoch nicht implementiert und darf nicht als vorhandene Schutzschicht behauptet werden. Zahlen und fachliche Aussagen bleiben deshalb ausschließlich in den strukturierten Engine-Feldern verbindlich; ein Narrativ ist optionaler Fließtext.

1.2 Herkunfts-Klassen pro Feld

HerkunftBedeutung
`engine`Deterministische Regel-/Geo-/Cost-Engine auf dem bereits assemblierten Input. Nie LLM.
`llm`Optionaler Text ausschließlich in scenarios[].narrativ für standard/premium; nicht verbindlich.
`static`Konstante aus Konfig/Recht, etwa Disclaimer-, Lizenz- und Tiertexte.
`external`1:1 extern übernommene Werte; soweit anwendbar quellen- und releasegebunden.

1.3 Determinismus & Caching

Bei gleichem assembliertem Layer-Input, Tier und Regelwerk erzeugt die Szenario-Engine dieselben Zahlen und Ampeln. Externe Abfragen, meta.generated_at, meta.as_of, Laufzeiten und optionale Narrative können zwischen zwei Reportläufen abweichen. Ein gespeicherter Report ist deshalb die stabile Momentaufnahme; es gibt keinen dokumentierten Cache-Schlüssel aus parcel_id, Engine- und Config-Version im aktuellen Top-Level-Contract.


1.4 Adresszuordnung als Voraussetzung (#561)

Neue Reportläufe benötigen einen vom österreichbeschränkten Geocoder bestätigten Standort. Eine leere Trefferliste liefert 422 address_not_found mit einem Hinweis zur Korrektur von Straße, Hausnummer, Postleitzahl und Ort in Österreich. Provider-/HTTP-/Timeout-/ungültige Antwortfehler liefern stattdessen 503 geocoding_unavailable; sie behaupten keinen Adressfehler. Beide Fälle enden vor Quellenabfragen, Szenarien und Reportpersistenz. Es entstehen weder Report-ID noch Permalink oder PDF. GET und POST der v1-API verwenden den Fehler-Envelope { error: { code, message } }; der Legacy-Endpunkt bewahrt seinen String unter error und ergänzt code. MCP reicht beide Zustände als Toolfehler weiter, einschließlich export_report.

Gültige AT-Treffer mit einzelnen fehlenden Datenebenen bleiben Teilreports mit meta.degraded. Ein unbekanntes Bundesland wird als at-unknown / „Bundesland nicht bestimmt“ ausgegeben. Historisch gespeicherte Reports mit meta.coordinate=null bleiben unverändert gespeichert; Web/PDF zeigen ihre Region neutral als „Nicht bestimmt“. Alte JSON-Snapshots werden nicht umgeschrieben.

Passen mehrere Adressen auf die Eingabe, fragt der Report-Pfad Nominatim einmal mit bis zu zehn Kandidaten ab und wählt den ersten Treffer in Niederösterreich, sonst den ersten überhaupt (ADR-0078, seit 05.10.2026). Web und PDF nennen die gefundene Adresse als „Gefunden: …“; die Felder meta.address.resolved und meta.address.ambiguous stehen in §3. Eine Adresse außerhalb Niederösterreichs liefert einen Teilreport ohne NÖ-Ebenen mit entsprechendem Hinweis.

Die vorhandenen Tageslimits zählen Anfragen, einschließlich fehlgeschlagener Adressversuche, sobald diese den bestehenden Auth-/Quota-Guard erreichen. Der atomare Zähler bleibt vor dem Geocoder; keine Erstattung und kein neuer Bucket. UI und direkte Reportseite lassen die Adresse nach Fehlern bearbeitbar und zeigen keinen Report-/Download-Erfolg. Prüfstand.

1.5 PV-Standortertrag (#562)

PV-Standortertrag (#562)

Variante A ist implementiert: Der Adressreport zeigt gespeicherten spezifischen PVGIS-Ertrag mit Modellannahmen. Neue Berechnungen erzeugen keine Leistung aus Parzellenfläche, keine 500-m²-Ersatzannahme und keine PV-Eurohochrechnung. Alte JSON-Snapshots bleiben unverändert; die Web-/PDF-/Kartensicht unterdrückt historische Gesamt- und Kostenwerte. Fehlende Annahmen werden nicht ergänzt. Vertrag und Abnahme dokumentieren API-Kompatibilität und die getrennten CI-/Preview-/Produktionsnachweise. #291 bleibt offen.

Neue solar-Snapshots tragen systemverlust_pct und modell_leistung_kwp. Neue PV-Szenarien tragen pv_report_mode: "specific_yield_v1", cost: null, cost_components: [] und ausschließlich den spezifischen Ertrag in kennzahlen, sofern vorhanden. Alte GET-/JSON-Exporte bleiben im historischen Vertrag erhalten.

2. Top-Level-Schema

```ts interface Report { meta: ReportMeta; parcel: ReportParcel; widmung: ReportWidmung | null; risiko: ReportRisiko; scenarios: ReportScenario[]; foerderungen: ReportFoerderung[]; ruleset: ReportRuleset; sources: SourceAttribution[]; disclaimer: ReportDisclaimer;

steckbrief?: ReportSteckbriefEintrag[]; standort_bilanz?: ReportStandortBilanz | null; solar?: ReportSolar | null; erreichbarkeit?: ReportErreichbarkeit | null; breitband?: ReportBreitband | null; laerm?: ReportLaerm | null; oev?: ReportOev | null; poi_dichte?: ReportPoiDichte | null; schutzgebiete?: ReportSchutzgebiete | null; denkmal?: ReportDenkmal | null; zweitwohnsitz?: ReportZweitwohnsitz | null; pendeln?: ReportPendeln | null; demografie?: ReportDemografie | null; boden?: ReportBoden | null; bahn?: ReportBahnanbindung | null; erdbeben?: ReportErdbeben | null; lcz?: ReportLcz | null; wlv?: ReportWlv | null; brachflaechen_potenzial?: ReportBrachflaechenPotenzial | null; } ```

Pflicht-Top-Level-Keys für alle Tiers sind meta, parcel, widmung, risiko, scenarios, foerderungen, ruleset, sources und disclaimer. meta.report_id und meta.permalink sind nur nach erfolgreicher Persistenz vorhanden. Es gibt aktuell weder schema_version noch eine zweite Top-Level-report_id; ebenso keine Top-Level-Blöcke bestand, grundbuch, confidence oder narrative.

`steckbrief` ist optional und rein abgeleitet: eine Ampel-Übersicht aller vorhandenen Indikator-Layer (Widmung, Hochwasser, Altlasten, Radon, Lärm, Schutzgebiete, Denkmal, Breitband) für den schnellen Blick oben im Report. Keine eigene Datenquelle, kein Gesamt-Verdikt. Einträge: `{ schluessel, label, ampel: gruen|gelb|rot|unbekannt, kurz }`; feste Reihenfolge, fehlende Layer werden ausgelassen (Lücke steht in `meta.degraded`). Die Altlasten-Ampel wird aus der release-gebundenen Punktprüfung abgeleitet: Adresspunkt von einer veröffentlichten Altlast mit ST_Covers abgedeckt → rot, nächste veröffentlichte Altlast innerhalb 1 km → gelb, sonst grün. Fehlt der qualifizierte Quellenvertrag, darf kein grünes Ergebnis entstehen.

`standort_bilanz` ist optional/null und rein abgeleitet aus dem `steckbrief`: ein kompakter Risiko-Rollup, den die Szenario-Engine als Entscheidungs-Kontext über den Nutzungsszenarien ausweist. Form: `{ rot: [{schluessel,label}], gelb: [{schluessel,label}], gruen_count: int, hinweis: string }`. Reine Zählung/Auflistung der nicht-grünen Indikatoren — KEIN Gesamt-Verdikt (eine einzelne „Gesamtampel" wäre über-claimend). null, wenn kein Indikator vorliegt.

ÖV-Kappung (M11, Fachlicher Entscheid 16.07.2026): Der Rollup ist der universelle „jede Nutzung"-Kontext. Eine ÖV-Erschließung ohne Güteklasse (oev.ampel == "rot") ist für die ÖV-nachrangigen Nutzungen Freiflächen-PV und Lager kein kritischer Standortfaktor und kann in diesem universellen Rollup nicht rot zählen — sie wird auf gelb (zu prüfen) gekappt. Der ÖV-Eintrag im steckbrief selbst bleibt rot (Rohsignal „keine Güteklasse"); nur sein Beitrag zur standort_bilanz wird gekappt, und sein kurz-Text weist die Nachrangigkeit aus. Die Kappung betrifft ausschließlich schluessel: "oev"; alle anderen roten Indikatoren zählen unverändert rot.


3. `meta` — Identität, Stichtag, Tier

FeldTypPflichtHerkunftBeschreibung
`meta.address.raw`stringjaexternalVom User eingegebene Adresse; wird dem Szenario-Narrativ nicht übergeben.
`meta.address.normalized`string | nulljaengineNormalisierte Geocoder-Adresse oder null, wenn keine Geokodierung gelang.
meta.address.resolved{ street, house_number, postcode, place } (je string | null) oder nulloptionalengineStrukturierte Fundadresse des gewählten Geocoder-Treffers (Nominatim addressdetails); Grundlage der Zeile „Gefunden: …“ in Web und PDF. Seit 05.10.2026 (ADR-0078); ältere Reports haben das Feld nicht.
meta.address.ambiguousbooleanoptionalenginetrue, wenn mehrere unterschiedliche Adressen auf die Eingabe passten; der Report-Pfad wählt dann den ersten Treffer in Niederösterreich, sonst den ersten österreichweit (ADR-0078). Fehlt in älteren Reports (= unbekannt).
meta.coordinateObjekt oder nulljaengineWGS84-lat/lon samt source: "nominatim" oder "bev"; bei fehlender Geokodierung null.
`meta.region.slug`stringjaengineRegionaler Slug, zum Beispiel at-noe; fail-safe Ableitung aus Geocoder/Adresse.
`meta.region.name`stringjaengineSichtbarer Regionsname.
`meta.report_tier`free/standard/premium/planerjaengineTechnisches Tier. Die provisorische Auflösung und fehlende Billing-Zusage stehen in §15.
`meta.as_of`ISO-8601 datejaengineReport-Stichtag des Laufs. Fachliche Quellenstände bleiben in den jeweiligen Layer-/Source-Feldern.
`meta.generated_at`ISO-8601 datetimejaengineGenerierungszeitpunkt.
`meta.degraded`{ layer, reason, fallback }[]jaengineAusgefallene, gesperrte oder unvollständige Layer; leer, wenn kein Degradierungsgrund vorliegt.
`meta.timings`{ layer, ms }[]optionalengineDiagnostische, nicht deterministische Laufzeiten. Überlappende Werte dürfen nicht summiert werden.
`meta.report_id`stringoptionalenginePermalink-ID nach erfolgreicher Persistenz.
`meta.permalink`stringoptionalengineTeilbarer Pfad /r/{report_id} nach erfolgreicher Persistenz.
`meta.saved`booleanoptionalengineKontogebundene dauerhafte Ablage; fehlend/false bedeutet nicht automatisch einen Persistenzfehler.

meta.org_id, meta.requested_by, meta.engine_version und meta.config_version sind im aktuellen ReportMeta nicht vorhanden. Die fachliche Ruleset-Version steht stattdessen unter ruleset.config_version.

`meta.degraded[]`-Element: `{ "layer": "geosphere", "reason": "source_not_released", "fallback": "Kein qualifizierter aktiver SPARTACUS-v3-Release verfügbar" }`. `meta.timings[]`-Element: `{ "layer": "parcel", "ms": 812 }`. Einzelne Layer laufen konkurrent; `layers_total` umfasst den gesamten Fan-out, `report_total` zusätzlich Szenario-Engine und ein gegebenenfalls erlaubtes Narrativ. Für den HTTP-Pfad liefert `/api/v1/report` außerdem den standardisierten `Server-Timing`-Header mit `guard`, `auth`, `session_tier`, `rate_limit`, `report_build`, bei POST `body_parse`/`persistence` und `total`. `X-LandNutzen-Timing` transportiert denselben Wert als namespaced Produktions-Fallback, falls ein vorgeschalteter Host den Standard-Header entfernt.


4. `parcel` — BEV-Kataster

Der qualifizierte Snapshot-Pfad liest ausschließlich den Active Release von bev-parzellen/public für public_display/point_lookup. Der erste Vertrag deckt nur bev-gaenserndorf-pilot-v1 mit der BBox [16.64,48.28,16.80,48.40] ab. Außerhalb kann ein getrennt behandelter BEV-WMS-Legacy-Pfad ausschließlich die Grundstücksidentität liefern; er erbt weder Release noch Snapshot-Geometrie. Der Neun-Bundesländer-Testkorpus belegt nur das Identitätsformat, keine österreichweite Datenabdeckung.

Neue Reports tragen die Lookup- und Provenienzfelder. Bereits gespeicherte Reports bleiben wegen der optionalen Felder lesbar.

FeldTypPflichtHerkunftBeschreibung
`parcel.found`booleanjaengineOb der Report eine verwendbare Parzellenidentität besitzt. Kann bei einem separaten WMS-Fallback auch dann `true` sein, wenn der qualifizierte Snapshot-Lookup `outside_coverage` oder `failed` meldet; es ist deshalb nicht mit einem Release-Treffer gleichzusetzen. `false` führt zum Teilreport.
`parcel.lookup_status`enum `hit`|`no_hit`|`outside_coverage`|`ambiguous_boundary`|`source_not_released`|`failed`optional; für neue ReportsengineÖffentliche Abbildung des release-gebundenen Lookups. Der interne Adapter bleibt fünfstufig und liefert Gate-Fehler technisch als `failed`; der Report projiziert Rechte-, Zweck-, Legacy-, Active-Release-, Freshness- und Quality-Gates auf `source_not_released`. Operative Provider-/Runtimefehler bleiben `failed`. `no_hit` ist nur innerhalb qualifizierter Coverage zulässig; Boundary-Mehrdeutigkeit wird nicht auf einen Treffer reduziert.
`parcel.lookup_failure_code`enum `rights_blocked`|`purpose_not_allowed`|`legacy_unverified`|`no_active_release`|`source_unavailable`|`freshness_rejected`|`quality_rejected`|`internal_error`bei `source_not_released` oder `failed`engineMaschinenlesbarer Grund ohne interne Rohdaten. Eine konservative Quarantäne-Envelope verhindert einen sonst ungesicherten `no_hit`; ein gültiger veröffentlichter Innentreffer hat Vorrang, ein bloßer Randtreffer neben Quarantäne bleibt `quality_rejected`/`source_not_released`. Operative Fehler werden nicht als Release-Gate umetikettiert.
`parcel.parcel_uid`stringbei qualifiziertem `hit`engineReversible interne LandNutzen-ID AT:{kg_code}:{parcel_number_normalized}; keine amtliche neue Kennung.
`parcel.source_release_id`UUIDbei release-gebundenem `hit`, `no_hit` oder `ambiguous_boundary`engineExakter qualifizierter Source Release des Snapshot-Ergebnisses. Fehlt beim getrennten WMS-Legacy-Pfad.
`parcel.match_method`enum `point_in_polygon`|`wms_getfeatureinfo`bei `found`, soweit verfügbarengineTatsächliche Matchmethode; Snapshot-Treffer verwenden `point_in_polygon`, Legacy-Fallbacks `wms_getfeatureinfo`.
`parcel.match_confidence`numberoptionalengineTechnische Matchkonfidenz 0..1; ersetzt keine Vermessungs- oder Rechtsauskunft.
`parcel.grundstuecksnummer`stringwenn `found`externalUnveränderter BEV-Quellalias der Grundstücksnummer.
`parcel.katastralgemeinde`stringwenn `found`externalKatastralgemeinde samt Nummer, soweit vom Bezugskanal geliefert.
`parcel.area_m2`number | nullnur beim qualifizierten Snapshot-TrefferengineAus der unverändert veröffentlichten DKM-Geometrie berechnete Fläche. Der WMS-Fallback liefert keine Fläche.
`parcel.area_tolerance_pct`number | nullnur beim qualifizierten Snapshot-TrefferstaticToleranzmarge DKM ≠ Grundbuch — Fachlicher Entscheid 11.08.2026 (#81): Grenzkataster null (vermessungsgenau, keine Angabe), sonst ±10 % Flächenschärfe.
`parcel.source`enum `dkm_mirror`|`wms`wenn `found`engineTatsächlicher Datenweg. `dkm_mirror` bezeichnet den release-gebundenen Snapshot-Pfad; `wms` den getrennten Identitätsfallback ohne qualifizierte Geometrie.
`parcel.kataster`enum `grenzkataster`|`grundsteuerkataster`|`unbekannt`wenn `found`externalKataster-Status aus BEV-RSTATUS. Anzeige (#320): Report, Karte und PDF nutzen katasterAnzeige() (lib/report/labels.ts (nicht im öffentlichen Doku-Portal verfügbar)); unbekannt wird sichtbar ausgewiesen. Keine Rechtsauskunft.
`parcel.bev_stand`ISO-8601 datebei release-gebundenem Ergebnis; bei WMS-IdentitätengineSnapshot: fachlicher Quellenstand des Active Release. WMS: Abrufdatum, weil dieser Kanal keinen qualifizierten Snapshot-Stand liefert.
`parcel.hinweis`stringwenn kein eindeutiger qualifizierter TrefferengineSichtbarer Hinweis für Grenze, Lücke, Mehrdeutigkeit oder Fehler.
`parcel.vermessung_disclaimer`stringsobald Parzellendaten angezeigt werdenstaticFixer Pflicht-Hinweis, dass DKM-Geometrie und -Fläche keine rechtsverbindliche Vermessung ersetzen.

Ein WMS-Treffer bewahrt den Snapshot-Zustand: außerhalb der Coverage outside_coverage, bei einem Release-Gate source_not_released, bei einem operativen Fehler failed. Der Fallback macht daraus nie einen qualifizierten Snapshot-Treffer; meta.degraded führt entsprechend ausserhalb_pilot_spiegelung, source_not_released oder request_fehlgeschlagen.

Der Report-JSON-Vertrag enthält keine Rohgeometrie und keine geometry_ref. ADR-0055 (nicht im öffentlichen Doku-Portal verfügbar) trennt den öffentlichen public_display/point_lookup strikt vom Pilotkanal bev-parzellen/export. Nur ein qualifizierter Export-Release für bev-gaenserndorf-pilot-v1 darf über parcel_versions_export_current Geometrie ausgeben.

Der bestehende Public-Snapshot umfasst 29.746 veröffentlichte Originalpolygone und 15 Quarantänefälle. Sein historischer ogr2ogr -spat-Filter übernimmt auch Pilotgrenzen schneidende Originalgeometrien vollständig; der separate Export übernimmt dagegen ausschließlich unveränderte ST_CoveredBy-Polygone. outsideCoverageRowCount beträgt 1.211 und macht alle nicht vollständig umschlossenen, ausgeschlossenen Public-Parzellen nachvollziehbar; 28.535 zulässige Originalpolygone sind im separaten Export-Pointer produktiv aktiviert. Public- und Export-Mengen sowie ihre Manifeste sind unabhängig gebunden. Ein im Pilot liegender Report-Standort allein öffnet keine teilweise oder vollständig außerhalb liegende Originalgeometrie; Clipping ist ausgeschlossen.

Der bestehende, rate-limitierte /api/parcel-geojson-Pfad verwendet parcel_geojson ausschließlich als internes Kartenoverlay eines bereits gespeicherten wertschöpfenden Standortreports; er ist kein eigenständiges BEV-Datenprodukt. Die verpflichtende reportId muss einen vorhandenen Report mit exakt übereinstimmender Koordinate und mindestens einer Nicht-BEV-Datenquelle bezeichnen. Fehlende oder unbekannte IDs bleiben 404, abweichende Standorte oder fehlender Mehrwert 403. Nur ein bestätigter hit mit derselben öffentlichen Release-ID und derselben öffentlichen Pointer-Version liefert die vorhandene FeatureCollection mit BEV-Attribution, Quellen-/Lizenzlink, Quellenstand, Änderungshinweis und Vermessungsgrenze. Unsichere Zustände erhalten 503; Erfolg und Fehler bleiben private, no-store. Vorhandene Reports werden nur gelesen; diese Prüfung erzeugt weder neue Reports noch weitere personenbezogene Produktionsschreibvorgänge.

Quarantänisierte Quellparzellen (ungültige BEV-Geometrie) werden nie ausgegeben; bekannt ist nur ihre Hülle (Bounding Box), die mehrere Quadratkilometer überdecken kann. Seit 05.10.2026 (#655) entscheiden Karte, PDF-Outline und DXF gleich und sperren die Parzelle nur am betroffenen Punkt statt pauschal für den ganzen Ausschnitt: Liegt der Adresspunkt in genau einer gültigen Parzelle, bleibt es ein hit, auch wenn eine Hülle Punkt und Ausschnitt überdeckt; metadata.quarantinedInWindow nennt die Zahl der Hüllen im Ausschnitt. Nur wenn der Punkt in keiner gültigen Parzelle oder auf einer Grenze liegt und eine Hülle ihn überdeckt, ist das Ergebnis quality_rejected (503 source_quality_rejected); schneidet eine Hülle nur den Ausschnitt, sperrt sie nicht. Einzelheiten: Vertrag, Abschnitt „Quarantäne“.

Die Report-PDF verwendet ausschließlich pdf_parcel_outline. Nur ein bestätigter hit zeichnet die Parzellenoutline (auch bei einer Quarantänehülle, die nur den Ausschnitt schneidet, siehe oben); bei no_hit, outside_coverage, ambiguous_boundary, quality_rejected, source_not_released oder failed bleibt sie null. Rechte, Freshness, Quality, Pilot-Coverage, Quarantäne und erfolgreicher Importlauf werden in einem einzigen atomaren SQL-Read geprüft; es gibt keinen Legacy- oder WMS-Geometrie-Fallback und keine Eigentümerdaten.

parcel_dxf ist im qualifizierbaren Rechtevertrag enthalten, code-seitig release_bound und seit dem Produktions- und Rollback-Readback vom 26. August 2026 kontrolliert aktiviert. DXF_EXPORT_ENABLED=true öffnet die Route nur gemeinsam mit dem qualifizierten Export-Release; der jederzeitige Rollbackzustand false liefert 404 und entfernt den Pfad aus dem öffentlichen OpenAPI-Schema. Der DXF-Aufruf verwendet denselben gespeicherten Mehrwertreport, dieselbe Koordinate und dieselbe Public-/Export-Parität und gibt nur vollständige, unveränderte Originalpolygone in voller Stützpunktdichte samt BEV-Attribution aus. Die schriftliche BEV-Klärung in #77 ist erfüllt; #412 ist mit positivem, negativem, Parser- und Rollback-Readback abgeschlossen. Der Export-Pointer ist produktiv aktiviert; GeoJSON-/PDF-/DXF-Consumer sind an denselben Release gebunden. Der öffentliche BEV-Exportvertrag beschreibt die vollständigen Rechte- und Aktivierungsgrenzen.


5. `widmung` — Flächenwidmung als Ampel

Quelle: generalisierte Widmungsumhüllenden des Landes Niederösterreich, release-gebundener Snapshot (widmung-noe, Migration 0058). Die Punktabfrage nutzt ST_Covers und verlangt einen vollständig in einer Kategorie liegenden 5-m-Kreis. Ein qualifizierter Nullbefund oder Grenzfall bleibt endgültig. Nur wenn der Snapshot nicht antworten kann, darf der separat freigegebene WMS-GetFeatureInfo-Pfad (widmung-noe-wms, RRU_WI_HUELLE) einspringen. Kein wöchentlicher Live-Abgleich und keine exakte Parzellenwidmung.

FeldTypPflichtHerkunftBeschreibung
`widmung.klasse`enum `bauland`|`gruenland`|`industrie`|`sonder`|`gemischt`|`unbekannt`jaengineAggregierte Hauptklasse.
`widmung.detail`string | nulljaexternalOriginal-Widmungstext NÖGIS oder null.
`widmung.ampel`enum `gruen`|`gelb`|`rot`jaengine`gruen`=Bauland baureif, `gelb`=eingeschränkt/aufschließungsbedürftig, `rot`=Grünland/Bauverbot.
`widmung.ampel_begruendung`stringjaengineRegelbasierter Kurztext (kein LLM), z. B. „Bauland-Wohngebiet, baureif".
`widmung.rechtsverbindlich`booleanjastaticImmer `false` — nur Indikation, keine rechtsverbindliche Auskunft (DATA-SOURCES §NÖGIS).
`widmung.quelle_stand`ISO-8601 date | nulljaengineStand der Widmung oder null, wenn kein belastbarer Stand vorliegt.
`widmung.source`enum `dkm_mirror`|`wms`optionalengineHerkunft: dkm_mirror = release-gebundener Snapshot der generalisierten Widmungsumhüllenden in PostGIS (historischer Feldwert, keine DKM-Parzellenwidmung); wms = separat freigegebener Live-Fallback über RRU_WI_HUELLE, nur wenn der Snapshot nicht antworten kann, nicht bei qualifiziertem Nullbefund/Grenzfall.

6. `risiko` — Naturgefahren, Altlasten, Klima

FeldTypPflichtHerkunftBeschreibung
`risiko.hochwasser.stufe`enum `keine`|`niedrig`|`mittel`|`hoch`|`unbekannt`jaenginePfad B (NÖGIS) implementiert (lib/geo/hochwasser.ts): Stufe aus der häufigsten Abflusszone — HQ30/HQ100 → hoch, HQ300 → niedrig, keine → keine. (HORA-Pfad A bleibt Lizenz-blockiert.)
`risiko.hochwasser.hq_zone`enum `HQ30`|`HQ100`|`HQ300`|nulljaengineHäufigste (ungünstigste) Abflusszone, in der der Punkt liegt (NÖ-OGD HYD_HW30/100/300ETC, PostGIS-Mirror oder WMS-GFI). null = keine kartierte Zone.
`risiko.hochwasser.jaehrlichkeiten`number[]jaengineAlle am Punkt vorhandenen Jährlichkeiten, aufsteigend (z. B. [30,100,300]). Der Quellwert 200 aus HYD_HW300ETC bleibt als 200 erhalten, klassifiziert aber nominal als HQ300.
`risiko.hochwasser.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaengineHQ30/HQ100 → rot; HQ300 → gelb; keine Zone → grün.
`risiko.hochwasser.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.hochwasser_indikator`.
`risiko.hochwasser.source`enum `mirror`|`wms`optionalengineTatsächlicher Datenweg. Optional für rückwärtskompatible, bereits gespeicherte Reports. Das Karten-Overlay bleibt unabhängig davon WMS.
`risiko.hochwasser.quelle_stand`ISO-8601 datejaengineMirror: amtlicher LASTUPDATE-Stand des aktiven Snapshots. WMS: Abrufdatum, falls der Punktdienst keinen belastbaren Stand liefert.
`risiko.altlasten.intersect`booleanjaenginetrue, wenn ST_Covers für den nach EPSG:31287 transformierten Adresspunkt und eine veröffentlichte Altlast-Geometrie gilt. Polygonkanten zählen als Treffer. Das Feld behauptet keinen Parzellen-Intersect.
`risiko.altlasten.nearby_m`number | nulljaengineMetrischer Abstand in der nativen Projektion EPSG:31287 zur nächsten veröffentlichten Altlast innerhalb 1 km; null, wenn im Radius keine liegt.
`risiko.altlasten.referenz`string | nulljaengineRückwärtskompatible quellenspezifische Referenz. Bei neuen WFS-Reports identisch mit ubaid; bei einem No-Hit null.
`risiko.altlasten.ubaid`string | nulloptionalengineKanonische eindeutige Identität der getroffenen beziehungsweise nächsten WFS-Fläche. ALTNR ist nicht eindeutig und wird nicht als Identität verwendet. Optional nur für die Lesbarkeit historisch gespeicherter Reports.
`risiko.altlasten.hinweis`stringjastaticFix, beginnt mit „Nicht-Listung garantiert keine Schadstofffreiheit." und trägt seit #317 den Rechtsstand: seit 1.1.2025 keine Verdachtsflächen (ALSAG-Novelle BGBl. I Nr. 30/2024); Erstabschätzungs-Flächen des GIS Altlasten (§ 18 Abs. 4 ALSAG) mangels offener Lizenz nicht enthalten (DATA-SOURCES §ALSAG, R-03).
`risiko.altlasten.quelle_stand`ISO-8601 date | nulljaengineHistorisches Kompatibilitätsfeld: alte INSPIRE-Reports behalten ihren damaligen Fachstand. Neue WFS-Reports setzen null, weil der Provider keinen fachlichen Stichtag liefert. Der technische Abruf darf hier nicht als Datenstand erscheinen.
`risiko.altlasten.source_release_id`UUIDbei neuem WFS-ReportengineExakter aktiver, qualifizierter Release, der Treffer, Nähe oder belastbaren No-Hit gemeinsam bindet.
`risiko.altlasten.retrieved_at`ISO-8601 timestampbei neuem WFS-ReportengineTechnischer WFS-Abrufzeitpunkt des aktiven Releases; ausdrücklich kein fachlicher Datenstand.
`risiko.altlasten.source_vintage`nullbei neuem WFS-ReportengineImmer null, solange der Umweltbundesamt-WFS keinen fachlichen Stichtag ausweist.
`risiko.altlasten.hist_nutzung`object | nulloptionalengineVisueller Indikator-Sub-Block (Issue #135). Keine algorithmische Klassifikation, kein LLM-Bilderverstehen — der Frontend-Layer zeigt nur die Karten.
`risiko.altlasten.hist_nutzung.zeitstaende[]`arrayjaengineListe der historischen Zeitstände. Wien-Pilot: 3 (Luftbildplan 1938 / 1956 / Orthofoto heute, alle WMTS `mapsneu.wien.gv.at`).
`risiko.altlasten.hist_nutzung.zeitstaende[].slug`enum `lb1938`|`lb1956`|`heute`jaengineStabile ID des Zeitstands.
`risiko.altlasten.hist_nutzung.zeitstaende[].jahr`intjaengineAufnahmejahr.
`risiko.altlasten.hist_nutzung.zeitstaende[].quelle`stringjaengineKlartext-Quelle (z. B. „data.wien.gv.at — Luftbildplan 1938").
`risiko.altlasten.hist_nutzung.zeitstaende[].verfuegbar`booleanjaengine`true` innerhalb Wien-BBox, sonst `false`.
`risiko.altlasten.hist_nutzung.zeitstaende[].layer_ref`string | nulljaengineMapLibre-Raster-Tile-URL (`{z}/{y}/{x}`); `null` wenn `verfuegbar=false`.
`risiko.altlasten.hist_nutzung.zeitstaende[].attribution`stringjaengineKurzattribution für Karten-Footer (CC BY 4.0).
`risiko.altlasten.hist_nutzung.hinweis`stringjastaticFix: „Historische Karten und Luftbilder werden als visueller Indikator angezeigt. Keine Schadstoffaussage, keine algorithmische Bewertung. Bei Verdachtsmomenten ist eine fachliche Bodenuntersuchung erforderlich."
`risiko.altlasten.hist_nutzung.coverage_hinweis`string | nulljaengineInnerhalb Coverage `null`, außerhalb Pilot-Beschränkungs-Hinweis.
`risiko.radon`object | nulloptionalengineRadon-Indikator-Block (Issue #132). `null` außerhalb Österreichs oder bei API-Ausfall (→ `degraded.radon`). Gemeinde-Ebene, keine Messung.
`risiko.radon.potenzialklasse`1 | 2 | 3 | nulljaengineÖNRAP-Klassifikation (1=niedrig, 2=mittel, 3=hoch).
`risiko.radon.rnv_status`enum `kein_schutz`|`vorsorge`|`schutz`|`unbekannt`jaengineRnV-Kategorie nach BGBl. II Nr. 470/2020 (rechtlich verbindlich).
`risiko.radon.rnv_gebiet_raw`string | nulljaengineRoh-Text aus der Quelle (audit-tauglich).
`risiko.radon.gemeinde`string | nulljaengineGemeinde-Name.
`risiko.radon.bezirk`string | nulljaenginePolitischer Bezirk.
`risiko.radon.gem_nr`number | nulljaengine5-stellige Gemeindekennzahl (Statistik Austria).
`risiko.radon.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaengineEngine-Mapping: Klasse 3 ODER Schutzgebiet → rot; Klasse 2 ODER Vorsorgegebiet → gelb; sonst grün.
`risiko.radon.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.radon_indikator_nicht_messung`.
`risiko.radon.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live-API gegen LFRZ).
`solar`object | nulloptionalengineStandortgenaues Solarpotenzial (ADR-0037 (nicht im öffentlichen Doku-Portal verfügbar)). `null` bei PVGIS-Ausfall → keine Ersatzkennzahlen. Die Abfrage hat ein Gesamtbudget von 10 s; bei Netzwerkfehler oder HTTP 5xx folgt genau ein Wiederholversuch nach 300 ms (nur, wenn dann noch mindestens 2 s Budget bleiben), nicht bei Timeout, 429, anderen 4xx oder ungültiger Antwort (lib/geo/solar.ts, #661).
`solar.spez_ertrag_kwh_pro_kwp`numberjaexternalPVGIS spez. Jahresertrag [kWh/kWp/Jahr] bei optimaler Ausrichtung (AC); keine Gesamtenergie.
`solar.einstrahlung_kwh_m2` / `solar.optimale_neigung_grad` / `solar.optimaler_azimut_grad`numberjaexternalEinstrahlung [kWh/m²] + von PVGIS bestimmte optimale Neigung/Azimut (0 = Süd).
`solar.quelle` / `solar.quelle_stand`stringjaexternalStrahlungs-DB (z. B. `PVGIS-SARAH3`) + Abrufdatum.
`solar.google`object | nulloptional (premium/planer)externalDachgenaue Google-Anreicherung (Building Insights). DC-Werte, Live-Overlay — vor Persistenz entfernt (stripGoogleSolar, ToS R-07); auf Permalinks abwesend. null = keine Coverage, kein Key oder nicht berechtigtes Tier.
`solar.google.max_kwp` / `.max_panels` / `.roof_segments`numberwenn `google`externalGrößte berechnete Belegung: kWp, Panel-Anzahl, Dachflächen.
`solar.google.max_yearly_kwh_dc` / `.implied_spez_kwh_pro_kwp_dc`numberwenn `google`externalJahresertrag (DC) + abgeleiteter spez. Ertrag (DC, ≠ PVGIS-AC).
`solar.google.imagery_quality` / `.imagery_date` / `.region_code`stringwenn `google`externalLuftbild-Qualität (HIGH/MEDIUM/BASE) + Datum + ISO-Regioncode.
`erreichbarkeit`object | nulloptionalengineErreichbarkeits-Score (#140, ADR-0036). Pro POI-Klasse der nächste kuratierte POI + Distanz + Fahrzeit. Env-gated: ohne `OSRM_BASE_URL` Luftlinien-Schätzung (`quelle: "luftlinie"`, Phase 2.0); mit gesetztem Backend echte Auto-Fahrzeit via OSRM `/table` (`quelle: "osrm"`, Phase 2.5), fail-open zurück auf Luftlinie bei Fehler/Timeout.
`erreichbarkeit.eintraege[]`arrayjaengine4 Einträge (autobahn, bahnhof, spital, schule), je mit Pfad zum nächsten kuratierten POI.
`erreichbarkeit.eintraege[].klasse`enum `autobahn`|`bahnhof`|`spital`|`schule`jaenginePOI-Kategorie.
`erreichbarkeit.eintraege[].label`stringjastaticAnzeige-Label (DE).
`erreichbarkeit.eintraege[].poi_id`string | nulljaengineStabile POI-ID aus `lib/maps/poi-at.ts` (nicht im öffentlichen Doku-Portal verfügbar).
`erreichbarkeit.eintraege[].poi_name`string | nulljaengineKlartext-Name des nächsten POI.
`erreichbarkeit.eintraege[].distanz_km`number | nulljaengineDistanz in km (1 Nachkommastelle). Bei `quelle: "osrm"` Straßendistanz, sonst Luftlinie.
`erreichbarkeit.eintraege[].fahrzeit_min`number | nulljaengineFahrzeit in Minuten. Bei `quelle: "osrm"` echte Routing-Fahrzeit, sonst Schätzung (Luftlinie × 1.35 / 50 km/h).
`erreichbarkeit.eintraege[].quelle`enum `luftlinie`|`osrm`|`ors`jaengineMethode: `luftlinie` ohne Routing-Backend (Default), `osrm` mit gesetztem `OSRM_BASE_URL`. Pro Klasse kann einzeln auf `luftlinie` zurückfallen, wenn OSRM den POI nicht erreicht.
`erreichbarkeit.hinweis`stringjastaticPflicht-Hinweis: „Indikator-Schätzung … keine echte Routing-Fahrzeit".
`erreichbarkeit.coverage_hinweis`stringjastaticHinweis auf kuratiertes POI-Set (Pilot-Phase).
`breitband`object | nulloptionalengineBreitband-Versorgungs-Indikator (RTR/Breitbandatlas, Festnetz). `null` außerhalb versorgter Zellen/Österreichs oder bei API-Ausfall (→ `degraded.breitband`). Summe aller Infrastruktur-Anbieter, keine Anschlusszusage. Live via WMTS-GetFeatureInfo (REST-API per 30.06.2025 eingestellt).
`breitband.max_download_mbit`number | nulljaengineMax. Download-Rate der 100×100-m-Rasterzelle (Mbit/s; `0` = „< 1").
`breitband.klasse`string | nulljaengineLegendenklasse des Breitbandatlas, abgeleitet aus max_download_mbit („< 30“, „≥ 30 bis < 100“, „≥ 100 bis < 300“, „≥ 300 bis < 1000 Mbit/s“, „≥ 1 Gbit/s“); null ohne Wert. Nicht der Klassentext der Quelle — der widerspricht dem Wert (Messung 04.10.2026). Gespeicherte Reports leiten die Klasse beim Lesen neu ab; Web und PDF nennen darunter den Wert.
`breitband.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaengineEngine-Mapping: ≥ 100 → grün; ≥ 30 → gelb; < 30 → rot; kein Wert → unbekannt.
`breitband.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.breitband_indikator`.
`breitband.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live WMTS-GFI gegen `breitbandatlas.gv.at`).
`breitband.foerderausbau`object | nulloptionalengine#322 (M4): Geförderter Ausbau (BBA2020/BBA2030) in derselben Zelle als Zukunftssignal — gesetzt, sobald ein Förderprojekt trifft, sonst fehlend/null. Verändert ampel/max_download_mbit nicht (Ist-Schwellen 100/30 bleiben). Eigener Layer Gefoerderter-Ausbau-Rasterzellen (WMTS-GFI), fail-soft. R-03: gefördert ≠ Anschluss-/Termin-Zusage.
`breitband.foerderausbau.programm`stringwenn gesetztengineProgramm + Netz-Modell, roh (z. B. „BBA2030: OpenNet").
`breitband.foerderausbau.technik`string | nullwenn gesetztengineAusbautechnik (z. B. „FTTP").
`breitband.foerderausbau.projektende`string | nullwenn gesetztengineGeplantes Projektende inkl. Statustext (z. B. „…2027 (Zelle gebaut)") — Plan, keine Zusage.
`breitband.foerderausbau.foerdernehmerin`string | nullwenn gesetztengineBauendes/förderndes Unternehmen.
`breitband.foerderausbau.is_connect`booleanwenn gesetztenginetrue = Connect (punktuelle Anschlussförderung, nicht zwingend Bau), sonst flächiger Rasterausbau.
`breitband.foerderausbau.meldung`stringwenn gesetztengineAnzeigefertiger R-03-Kopfsatz (Web+PDF identisch); zwei Varianten nach is_connect.
`laerm`object | nulloptionalengineUmgebungslärm-Indikator (strategische EU-Lärmkarten, Kartierungsrunde 2022, END 2002/49/EG). Der aktuelle Vertrag umfasst vier Quelltypen: Straße, Schiene, Flug sowie Industrie/IPPC; IPPC ist nur in den ausgewiesenen Ballungsräumen kartiert. Kompatibilitätsregel: Nur wenn alle acht Flug-/IPPC-Felder als unteilbares Acht-Felder-Tupel vorhanden sind, darf ein Consumer den Vier-Quellen-Scope behaupten. Fehlt auch nur eines, ist der gespeicherte Altbericht fail-closed ausschließlich als Straße/Schiene zu kennzeichnen; Flug/IPPC dürfen weder als geprüft noch als „ohne Treffer“ erscheinen. `null` nur bei nicht verfügbarem/ungeprüftem Live-Pfad (→ `degraded.laerm`); „keine Zone" ist ein gültiges Ergebnis innerhalb des ausgewiesenen Scopes (Ampel grün). Berechnete Mittelungspegel in 4 m Höhe, keine Messung und kein 10-m-Raster. Der WMS-Pfad ist an einen qualifizierten Source-Release mit Live-Checks für Report, Karte und Legende gebunden. Quelle: www.laerminfo.at, Lizenz: CC BY 4.0, Attribution exakt „Datenquelle: www.laerminfo.at“.
`laerm.strasse_lden_db`number | nulljaengineUntergrenze des Straßen-Lden-Bandes in dB (z. B. 65 für 65–69); `null` = keine Zone.
`laerm.strasse_lden_klasse`string | nulljaengineRoh-Klassenbezeichner der Quelle (z. B. „Lden6569").
`laerm.strasse_lnight_db`number | nulljaengineUntergrenze des Straßen-Lnight-Bandes (Nacht).
`laerm.strasse_lnight_klasse`string | nulljaengineRoh-Klassenbezeichner Straße Nacht.
`laerm.schiene_lden_db`number | nulljaengineUntergrenze des Schienen-Lden-Bandes.
`laerm.schiene_lden_klasse`string | nulljaengineRoh-Klassenbezeichner Schiene Tag-Abend-Nacht.
`laerm.schiene_lnight_db`number | nulljaengineUntergrenze des Schienen-Lnight-Bandes (Nacht).
`laerm.schiene_lnight_klasse`string | nulljaengineRoh-Klassenbezeichner Schiene Nacht.
`laerm.flug_lden_db`number | nulloptional (Altberichte)engineUntergrenze des Flug-Lden-Bandes; `null` = keine kartierte Fluglärm-Zone.
`laerm.flug_lden_klasse`string | nulloptional (Altberichte)engineRoh-Klassenbezeichner Flug Tag-Abend-Nacht.
`laerm.flug_lnight_db`number | nulloptional (Altberichte)engineUntergrenze des Flug-Lnight-Bandes (Nacht).
`laerm.flug_lnight_klasse`string | nulloptional (Altberichte)engineRoh-Klassenbezeichner Flug Nacht.
`laerm.industrie_lden_db`number | nulloptional (Altberichte)engineUntergrenze des Industrie-/IPPC-Lden-Bandes; IPPC-Zonen sind nur in ausgewiesenen Ballungsräumen verfügbar.
`laerm.industrie_lden_klasse`string | nulloptional (Altberichte)engineRoh-Klassenbezeichner Industrie/IPPC Tag-Abend-Nacht.
`laerm.industrie_lnight_db`number | nulloptional (Altberichte)engineUntergrenze des Industrie-/IPPC-Lnight-Bandes (Nacht).
`laerm.industrie_lnight_klasse`string | nulloptional (Altberichte)engineRoh-Klassenbezeichner Industrie/IPPC Nacht.
`laerm.max_lden_db`number | nulljaengineHöchste Lden-Untergrenze über den beim Erstellen geprüften Scope (aktuell Straße, Schiene, Flug und Industrie/IPPC; in Altberichten ausschließlich Straße/Schiene); `null` = keine Zone innerhalb dieses Scopes.
`laerm.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaengineEngine-Mapping: Lden ≥ 70 ODER Lnight ≥ 60 → rot; sonst kartierte Zone → gelb; keine Zone → grün.
`laerm.hinweis`stringjastaticNeue Reports sind wortgleich mit `disclaimer.bausteine.laerm_indikator` und enthalten verbindlich: „Die Ampel ist ein Screening auf Basis der EU-Lärmkarten und ersetzt kein schalltechnisches Gutachten; Ansprüche auf Schallschutzmaßnahmen richten sich nach eigenen, teils niedrigeren Grenzwerten.“ Gespeicherte Altberichte können einen historischen Freitext enthalten (unter anderem die überholte „10-m-Raster“-Behauptung). Sichtbare Consumer dürfen diesen Text nicht ungeprüft wiedergeben: Sie normalisieren ihn auf einen aktuellen, Straße/Schiene-spezifischen Altbericht-Hinweis mit genau diesem R-03-Satz; Messwerte und Ampel des Snapshots bleiben unverändert.
`laerm.quelle_stand`ISO-8601 datejaengineAbrufdatum des release-gebundenen Live-WMS-GFI gegen `inspire.lfrz.gv.at`; ausdrücklich nicht Datenjahr oder Kartierungsstand (der ist 2022).
`oev`object | nulloptionalengineÖV-Güteklassen-Indikator (ÖROK/BMIMI-Modell, #101 Phase 2). Nur PostGIS-Mirror (Pilot-bbox, jährliche Datei-Lieferung über AustriaTech — kein amtlicher WMS/WFS): außerhalb der Spiegelung `null` (→ `degraded.oev`, reason `ausserhalb_pilot_spiegelung`). „Keine Güteklasse" am Punkt ist ein gültiges Ergebnis (Ampel rot).
`oev.gueteklasse`string | nulljaengineGüteklasse `A` (beste) … `G`; `null` = keine Klasse am Punkt. Klassen-Polygone überlappen — die beste Klasse gewinnt.
`oev.ampel`enum `gruen`|`gelb`|`rot`jaengineDeterministisch (`classifyOev`): A–C → grün, D–G → gelb, keine Klasse → rot. Schwellen am 16.07.2026 vom Cofounder bestätigt (M11); offene Rot-Differenzierung bei Bedarfsverkehr in #341.
`oev.ampel_begruendung`stringjaengineKlartext-Begründung der Ampel (deterministisch, kein LLM).
`oev.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.oev_indikator`.
`oev.quelle_stand`ISO-8601 date | nulljaengineFahrplan-Referenzdatum der Lieferung (aus der Spiegelung; nie das Report-Datum).
`oev.stichtag_typ`string | nulloptionalengineTyp des Fahrplan-Stichtags der Pilotlieferung (z. B. „Werktag in den Herbstferien“ für 29.10.2025); null bei unbekanntem Stand. Fehlt in Berichten vor dem 04.10.2026.
`oev.standort_check_stand`ISO-8601 date | nulloptionalengineStichtag der österreichweiten Lieferung, die der kostenlose Standort-Check liest (nur Vergleich, keine Klasse daraus); null ohne lesbaren aktiven Release. Fehlt in Berichten vor dem 04.10.2026.
`oev.stichtag_hinweis`stringoptionalengineSichtbarer Hinweis zu Stichtag und möglicher Abweichung zum Standort-Check, wortgleich in Web und PDF (oevStichtagHinweis). Fehlt in Berichten vor dem 04.10.2026; deren Darstellung bleibt unverändert.
`oev.source`const `mirror`jaengineHerkunfts-Transparenz (analog `parcel.source`).
`schutzgebiete`object | nulloptionalengineSchutzgebiete-Indikator (NÖ-OGD Natur- + Wasserschutz). null außerhalb NÖ oder bei API-Ausfall (→ `degraded.schutzgebiete`). „Kein Treffer" ist ein gültiges Ergebnis (grün). Keine Rechtsauskunft. Live via WMS-GetFeatureInfo.
`schutzgebiete.treffer[]`arrayjaengineAlle zutreffenden Designationen (überlappende möglich).
`schutzgebiete.treffer[].kategorie`stringjaengineStabile ID (z. B. naturschutzgebiet, europaschutz_ffh, landschaftsschutzgebiet, nationalpark, naturpark, wasserschutzgebiet, wasserschongebiet).
`schutzgebiete.treffer[].label`stringjaengineAnzeige-Label (DE).
`schutzgebiete.treffer[].name`string | nulljaengineGebietsname aus der Quelle.
`schutzgebiete.treffer[].schwere`enum `gelb`|`rot`jaenginerot = streng (Naturschutz-/Europaschutz-/Nationalpark-/Wasserschutzgebiet); gelb = milder (Landschaftsschutz/Naturpark/Wasserschongebiet).
`schutzgebiete.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaengineUngünstigste Schwere über alle Treffer; grün wenn keiner.
`schutzgebiete.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.schutzgebiete_indikator`.
`schutzgebiete.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live WMS-GFI gegen sdi.noe.gv.at).
`denkmal`object | nulloptionalengineDenkmalschutz (BDA-Denkmalverzeichnis §3 DMSG, NÖ). null, wenn keine Parzelle (KG+GstNr) aufgelöst wurde. Parzellenscharfer Match gegen vorgenerierte Liste (kein Netz/DB). Keine Rechtsauskunft; Ensembles/Umgebungsschutz nicht erfasst.
`denkmal.gelistet`booleanjaengineLiegt das Grundstück (KG-Code + GstNr) direkt im Denkmalverzeichnis?
`denkmal.objekte[]`arrayjaengineAuf der Parzelle gelistete Objekte (titel, schutzart).
`denkmal.gemeinde`string | nulljaengineGemeinde (aus KG-Code abgeleitet).
`denkmal.gemeinde_count`number | nulljaengineAnzahl denkmalgeschützter Objekte in der Gemeinde (Kontext).
`denkmal.ampel`enum `gruen`|`gelb`|`rot`|`unbekannt`jaenginegelistet → gelb; sonst grün. M8 (16.07.2026): Denkmalschutz ist ein Bewilligungsvorbehalt (Veränderung/Restaurierung/Abbruch BDA-pflichtig), kein Standort-Risiko — „Auflagen, aber Nutzung möglich". Für ein Reaktivierungs-Tool sind Denkmäler oft Zielobjekte; deshalb nie rot.
`denkmal.nachbarn[]`arrayjaengineDenkmalgeschützte Nachbarparzellen im 14-m-Umkreis (Rand-zu-Rand; Fachlicher Entscheid 11.08.2026, #326: Nachbarbegriff baubehördlicher Verfahren/Parteistellung). Je Eintrag kg, gnr, distanz_m (0 = direkt angrenzend), objekte[]. Release-gebundener Mirror-Lookup (lookupParcelNeighborsMirror, Capability point_lookup) + In-Memory-BDA-Match. Rein informativ, keine Wirkung auf ampel (M8). Nur aussagekräftig bei nachbarn_geprueft=true.
`denkmal.nachbarn_geprueft`booleanjaenginefalse = Prüfung nicht möglich (Parzelle außerhalb des release-gebundenen Pilot-Spiegels oder Lookup nicht verfügbar) — dann ist nachbarn: [] keine Aussage und wird als „nicht geprüft“ angezeigt, nie als „keine Denkmäler in der Nachbarschaft“.
`denkmal.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.denkmal_indikator`.
`denkmal.quelle_stand`ISO-8601 datejaengineAbrufdatum (Lookup gegen die committete BDA-Liste).
`wlv`object | nulloptionalengineGefahrenzonenplan Wildbach & Lawine (WLV/BML, LFRZ WMS-GFI). null bei API-Ausfall (→ `degraded.wlv`); „keine Zone" ist ein gültiges Ergebnis (grün). Nur Gemeinden mit Plan (alpiner Raum). Keine rechtsverbindliche Zonen-Auskunft.
`wlv.zonen[]`string[]jaengineDistinkte Objektart-Werte am Punkt (z. B. „Rote Zone Wildbach", „Summenzone - Gelbe Zone Wildbach", „Raumrelevanter Bereich").
`wlv.ampel`enum `gruen`|`gelb`|`rot`jaengineRote Zone → rot; echte erhobene Zone (Gelbe/Summen-/Blaue/Braune) → gelb; keine Zone oder ausschließlich Raumrelevanter Bereich → grün. M10 (16.07.2026): Der Raumrelevante Bereich ist keine erhobene Gefahrenzone, sondern ein Planungs-Hinweis → keine Warnfarbe.
`wlv.raumrelevanter_bereich`booleanjaengineM10: Punkt liegt in einem Raumrelevanten Bereich der WLV. Treibt in UI/PDF einen eigenen ℹ️-Hinweis statt einer Warnfarbe; unabhängig von `ampel` (liegt zusätzlich eine echte Zone vor, führt diese die Ampel, der Hinweis bleibt Kontext).
`wlv.gemeinde`string | nulljaengineHauptgemeindename des Gefahrenzonenplans.
`wlv.revisionsjahr`number | nulljaengineRevisionsjahr des Plans (Daten-Vintage).
`wlv.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.wlv_indikator`.
`wlv.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live WMS-GFI gegen inspire.lfrz.gv.at/000901).
`pendeln`object | nulloptionalenginePendler-/Arbeitsplatzindikator der GEMEINDE (#363; Statistik Austria, Abgestimmte Erwerbsstatistik + Arbeitsstättenzählung, Neon-Mirror stat_aest_gemeinde). Neutraler Info-Block ohne Ampel (Klima-Präzedenz): reine Statistik, kein Steckbrief-/Bilanz-Eintrag. null ohne Gemeindebezug (gem_nr aus dem Radon-Layer fehlt → `degraded.pendeln`, reason `keine_gemeinde`) oder wenn die Gemeinde nicht im Datenstand liegt (reason `gemeinde_nicht_im_datenstand`; Wien ist nach Gemeindebezirken codiert).
`pendeln.gemeinde` / `pendeln.gem_nr`string / numberjaengineGemeindename + 5-stellige Statistik-Austria-GKZ.
`pendeln.jahr`intjaengineBerichtsjahr der AEST (Stichtag 31.10.; ~1,5 Jahre Publikations-Lag).
`pendeln.auspendler_pct`number | nulljaengineAuspendler:innenanteil an den Erwerbstätigen am Wohnort (%).
`pendeln.erwerbstaetigenquote_pct` / `pendeln.arbeitslosenquote_pct`number | nulljaengineErwerbstätigenquote 15–64 / Arbeitslosenquote 15+ (%).
`pendeln.arbeitsstaetten` / `pendeln.besch_am_arbeitsort` / `pendeln.unternehmen` / `pendeln.bev_gemeinde`number | nulljaengineArbeitsort-Kennzahlen der Gemeinde (Arbeitsplatzdichte-Kontext).
`pendeln.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.pendeln_indikator`.
`pendeln.source`const `mirror`jaengineDatenweg-Kennung (Neon-Mirror, kein Live-Call).
`pendeln.quelle_stand`ISO-8601 datejaengineDaten-Vintage des Mirrors (max(stand) der Tabelle) — nie das Abfragedatum.
`demografie`object | nulloptionalengineBevölkerungsdynamik der GEMEINDE (#364; Statistik Austria, Bevölkerungsstand zum 1.1. + Wanderungsstatistik, Neon-Mirror stat_bev_gemeinde/stat_wanderung_gemeinde). Neutraler Info-Block ohne Ampel. Eine Geburten-/Sterbebilanz je Gemeinde existiert als OGD nicht und wird bewusst nicht ausgewiesen (Discovery #364, R-03). null-/degraded-Semantik wie `pendeln`.
`demografie.gemeinde` / `demografie.gem_nr`string / numberjaengineGemeindename + 5-stellige Statistik-Austria-GKZ.
`demografie.stichtag` / `demografie.bev`ISO-8601 date / numberjaengineAktuellster Bevölkerungsstand zum 1.1.
`demografie.bev_vor_1j` / `demografie.bev_vor_5j` / `demografie.delta_1j` / `demografie.delta_5j_pct`number | nulljaengineVergleichsbestände + Deltas; null, wenn das Vergleichsjahr nicht im Mirror liegt (kein erfundener Trend).
`demografie.wanderung`object | nulljaengineJüngstes Jahr mit vollständigem Saldo: `{ jahr, binnen_saldo, aussen_saldo, gesamt_saldo }` — Zu-/Wegzüge über die Gemeindegrenze (gemeindeinterne Umzüge ausgenommen); Außenwanderung liegt als OGD erst ab 2015 vor.
`demografie.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.demografie_indikator`.
`demografie.quelle_stand`ISO-8601 datejaengineDaten-Vintage des Mirrors — nie das Abfragedatum.
`demografie.source`const `mirror`jaengineDatenweg-Kennung (Neon-Mirror, kein Live-Call).
`boden`object | nulloptionalengineBFW-Bodenkarte eBOD am Punkt (#360; Gründigkeit + Wasserverhältnisse, PostGIS-Pilot-Mirror boden, Migrationen 0031 + 0033). Beide Themen gehören zu genau einem qualifizierten, unveränderlichen Erfolgs-Snapshot und werden atomar gemeinsam veröffentlicht. Neutraler Info-Block ohne Ampel. EHRLICHKEITS-REGEL: `kartiert=false` ist eine Abdeckungslücke (die eBOD erfasst nur landwirtschaftlich genutzte Flächen — typisch bebautes Gebiet), KEIN Befund über den Boden; der Block wird dann trotzdem gerendert. `null` außerhalb der Pilot-Spiegelung (→ `degraded.boden`, reason `ausserhalb_pilot_spiegelung`).
`boden.kartiert`booleanjaengineLiegt am Punkt ein eBOD-Polygon (mind. ein Thema)?
`boden.gruendigkeit` / `boden.wasserverhaeltnisse`string | nulljaengineKlartext-Klassen der BFW-Kartierung (z. B. „mittelgründig“, „trocken“). Treffen am randinklusiven Punkt mehrere unterschiedliche Klassen, enthält der String alle Werte mit dem Präfix „Mehrdeutig an Kartengrenze“; keine Klasse wird bevorzugt.
`boden.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.boden_indikator` (Landwirtschafts-Abdeckung, historisches Erhebungsalter, kein Bodengutachten).
`boden.quelle_stand`ISO-8601 datejaengineAtom-Feed-Stand der eBOD-Lieferung aus dem Mirror — nie das Abfragedatum.
`boden.source`const `mirror`jaengineDatenweg-Kennung (PostGIS-Mirror, kein Live-Call).
`bahn`object | nulloptionalengineBahnanbindung aus dem ÖBB-GTFS-Soll-Fahrplan (#380; CC BY 4.0, österreichweiter Snapshot gtfs_halt, Migration 0036): nächste Station im 5-km-Umkreis mit der GEMESSENEN Bedienung. Neutraler Info-Block ohne Ampel — ergänzt `oev` (ÖROK-Güteklasse = Modellwert mit Ampel), ersetzt ihn nicht. ⚠ Nur Schienenverkehr: Regionalbusse und Verbundlinien sind im Feed nicht enthalten. null, wenn keine Station im Umkreis liegt (→ `degraded.bahn`, reason `keine_station_im_umkreis`).
`bahn.station` / `bahn.distanz_m`string / numberjaengineStationsname + Luftlinie in Metern (kein Gehweg) — wird immer mit ausgewiesen.
`bahn.halte_werktag` / `bahn.halte_samstag` / `bahn.halte_sonntag`numberjaengineGeplante Halte an je einem Referenztag (Median-Tag des Fahrplanjahres nach aktiven Services), beide Richtungen, alle Bahnsteige der Station. Der ÖBB-Feed steuert den Betriebstag über calendar_dates.txt; eine reine calendar.txt-Summe ergäbe Werte, die an keinem Tag zutreffen (#380).
`bahn.spitzenstunde` / `bahn.spitzenstunde_halte`number | nulljaengineStunde (0–23) mit den meisten Werktags-Halten und deren Anzahl.
`bahn.top_ziele`arrayjaengineHäufigste Fahrtziele ab dieser Station als `{ ziel, halte }[]`; Züge, die AN dieser Station enden, sind ausgenommen.
`bahn.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.bahn_indikator` (Bus-Lücke, Regelfahrplan, Luftlinie).
`bahn.quelle_stand`ISO-8601 datejaengineFahrplanjahr-Start der Lieferung — nie das Abfragedatum.
`bahn.source`const `mirror`jaengineDatenweg-Kennung (Snapshot-Mirror, kein Live-Call).
`erdbeben`object | nulloptionalengineErdbebenzone nach ÖNORM B 1998-1 (#427; GeoSphere Austria, CC BY 4.0, österreichweiter Snapshot erdbeben_ort, Migration 0045, release-gebunden nach FND-03). Ampel per fachlichem Entscheid 11.08.2026 (#453): ampel + ampel_begruendung, Zone 0–1 grün · 2–3 gelb · 4 rot (classifyErdbeben); ausdrücklich keine Kostenwirkung auf die Szenarien; kein Steckbrief-/Bilanz-Eintrag (Bilanz-Einbindung nicht entschieden, Präzedenz M11). null außerhalb der Abdeckung (→ `degraded.erdbeben`, reason `ausserhalb_abdeckung`).
`erdbeben.zone`number (0–4)jaengineGefahrenzone nach ÖNORM B 1998-1. Zone 0 ist ein gültiger Wert (niedrigste Stufe), kein Leerwert.
`erdbeben.ort` / `erdbeben.plz` / `erdbeben.distanz_m`string / number / numberjaengineDer Ort, dem die Norm diese Zone zuordnet, samt Luftlinie in Metern. Steht IMMER dabei: Die ÖNORM ordnet je Ort zu, nicht je Grundstück — das Ortsnetz hat einen Median-Nachbarabstand von 3,3 km.
`erdbeben.ega` / `erdbeben.pga`numberjaengineReferenzwerte der Quelle, bewusst uninterpretiert und mit Feldnamen ausgewiesen. Was sie fachlich genau bezeichnen, ist aus der Quelle nicht dokumentiert (offener Punkt im Rechte-Review); beobachtet gilt durchgängig PGA = EGA × √2.
`erdbeben.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.erdbeben_indikator` (Ortsbezug, 2-km-Kartengenauigkeit, Zuständigkeit des Tragwerksplaners).
`erdbeben.quelle_stand`ISO-8601 datejaengineVeröffentlichungsstand des Quell-Items — nie das Abfragedatum.
`erdbeben.source`const `mirror`jaengineDatenweg-Kennung (Snapshot-Mirror, kein Live-Call).
`lcz`object | nulloptionalengineUmgebungsstruktur nach Local Climate Zones (#454; Demuzere et al. 2022, CC BY 4.0, 5×5-Zellen-Aggregat lcz_umgebung (~500 m N-S × ~335 m O-W), Migration 0046, release-gebunden nach FND-03): modale LCZ-Klasse eines 5×5-Zellen-Fensters um die Adresse (Gradraster: rund 500 m Nord-Süd × 335 m Ost-West). Neutraler Info-Block ohne Ampel — Strukturbeschreibung der NACHBARSCHAFT (Stewart & Oke: „local“ = Nachbarschaftsskala), keine Parzellenaussage, keine Klimamessung; kein Steckbrief-/Bilanz-Eintrag. null außerhalb des Österreich-Fensters (→ `degraded.lcz`, reason `ausserhalb_abdeckung`).
`lcz.klasse`number (1–17)jaengineModale LCZ-Klasse des Fensters (Stewart & Oke 2012). Web/PDF zeigen die neutral-deskriptive deutsche Beschreibung IMMER zusammen mit dem offiziellen englischen Klassennamen (LCZ_KLASSEN in labels.ts — redaktionelle Setzung, es existiert kein etabliertes Laienvokabular).
`lcz.anteil` / `lcz.n_valid`number / numberjaengineEinhelligkeit des Fensters: Zellen der Modalklasse von maximal 25 gültigen Rasterpixeln (~100 m N-S × ~67 m O-W). Ein heterogenes Fenster (z. B. 11/25) wird sichtbar ausgewiesen, nie geglättet.
`lcz.konfidenz`number (0–100)jaengineMittlere ML-Modell-Konfidenz (%) der Modal-Pixel aus dem Probability-Layer der Quelle — die Aggregations-Entscheidung (#454): Einzelpixel-Konfidenz streut 38–100 %, das Fenster-Mittel ist die ehrliche Angabe.
`lcz.distanz_m`numberjaengineLuftlinie zum Mittelpunkt der Gitterzelle (max. ~304 m konstruktionsbedingt — die Zellen sind anisotrop).
`lcz.hinweis`stringjastaticWortgleich mit `disclaimer.bausteine.lcz_indikator` (ehrliche Fenstergeometrie ~500 × 335 m, Nachbarschaftsaussage, ML-Herkunft, Nominaljahr 2018).
`lcz.quelle_stand`ISO-8601 datejaengineNominaljahr der Karte (2018-01-01) — nie das Abrufdatum; Darstellung zeigt das Jahr.
`lcz.source`const `mirror`jaengineDatenweg-Kennung (Snapshot-Mirror, kein Live-Call).

6.1 `risiko.klima` — release-gebundener SPARTACUS-v3-Snapshot

Sichtbarer Titel: „SPARTACUS v3 · Datenstand 2025 · 1-km-Raster". Die Attribution wird wortgleich als „Datenquelle: GeoSphere Austria - https://data.hub.geosphere.at" ausgegeben. Der Pflicht-Hinweis lautet: „Das 1-km-Raster ist ein regionaler Klimaindikator und weder Messung noch Prognose für eine einzelne Parzelle."

Neue Reports liefern den Klimawert ausschließlich aus dem aktiven, qualifizierten Release geosphere-spartacus-v3 und befüllen alle unten aufgeführten Provenienzfelder. Diese zusätzlichen Felder bleiben im JSON-Contract optional, damit bereits gespeicherte Reports ohne v3-Provenienz weiterhin lesbar sind. Im API-Schema gelten sie als unteilbares Alles-oder- Nichts-Tupel: Fehlt auch nur eines, ist der Block kein gültiger v3-Report und ein Client darf weder SPARTACUS v3 noch einen konkreten Release nachträglich behaupten.

FeldTypPflichtHerkunftBeschreibung
`risiko.klima.jahresmittel_temp_c`numberjaengineSPARTACUS-Jahresmittel der Lufttemperatur in °C.
`risiko.klima.jahresniederschlag_mm`numberjaengineSPARTACUS-Jahressumme des Niederschlags in mm.
`risiko.klima.sonnenstunden_jahr`numberjaengineSPARTACUS-Jahressumme der Sonnenscheindauer in Stunden; keine Einstrahlung und keine Grundlage für eine PV-Eignungs- oder Ertragsentscheidung.
`risiko.klima.referenzjahr`intjaengineAbgeschlossenes Kalenderjahr des Rasterwerts. Der qualifizierte v3-Snapshot verwendet 2025; gespeicherte Alt-Reports behalten ihr ursprüngliches Jahr.
`risiko.klima.quelle_stand`ISO-8601 datejaengineRückwärtskompatibler Anzeige-Stichtag. Die belastbare Snapshot-Provenienz steht zusätzlich in source_vintage und retrieved_at.
`risiko.klima.source_slug`const `geosphere-spartacus-v3`optionalengineRegistry-Schlüssel der tatsächlich gelesenen Quelle.
`risiko.klima.source_release_id`UUIDoptionalengineID des beim atomaren Lookup aktiven, qualifizierten Releases.
`risiko.klima.release_key`UUIDoptionalengineDeterministischer fachlicher Release-Schlüssel.
`risiko.klima.dataset_version`const `3`optionalengineSPARTACUS-Hauptversion; als String serialisiert.
`risiko.klima.transform_version`const `spartacus-v3-snapshot-v1`optionalengineVersion der deterministischen Artefakt-Transformation.
`risiko.klima.source_vintage`stringoptionalengineFachlicher Datenstand des qualifizierten Snapshots, aktuell 2025.
`risiko.klima.retrieved_at`ISO-8601 date-timeoptionalengineAbrufzeitpunkt des unveränderlich nachgewiesenen Quellartefakts.
`risiko.klima.resolution_m`const `1000`optionalengineNative Rasterauflösung in Metern.
`risiko.klima.native_crs`const `EPSG:3416`optionalengineKoordinatenreferenzsystem der Rasterzentren.
`risiko.klima.easting_m` / `risiko.klima.northing_m`int / intoptionalengineZentrum der tatsächlich verwendeten 1-km-Rasterzelle in EPSG:3416.
`risiko.klima.attribution`stringoptionalengineWortgleiche sichtbare Attribution: „Datenquelle: GeoSphere Austria - https://data.hub.geosphere.at".
`risiko.klima.attribution_url`URLoptionalengineSichtbarer Quellenlink https://data.hub.geosphere.at.
`risiko.klima.license`const `CC-BY-4.0`optionalengineLizenzkennung des aktiven Releases.
`risiko.klima.license_url`URLoptionalengineLink zur GeoSphere-Lizenzinformation.
`risiko.klima.terms_url`URLoptionalengineSichtbarer Link zu den GeoSphere-Nutzungsbedingungen.
`risiko.klima.boundary`stringoptionalstaticWortgleicher Pflicht-Hinweis zur regionalen Aussagegrenze des 1-km-Rasters.

6.2 `risiko.hitzebelastung` — historische GeoSphere-HORA-Schwellentage

Der optionale Block ist nur bei einer eigenständigen qualifizierten Freigabe der Quelle geosphere-hora-hitzelayer zulässig. Sein Ursprung ist ausschließlich die CC-BY-4.0-Distribution hora_hitzelayer-v1-1km für die historische Klimaperiode 1991–2020; HORA 3.0, HORA-Pass und LFRZ bleiben gesperrt.

Der aktive Quellenrelease ist im Produktionsnachweis rückgelesen. Derselbe Nachweis dokumentiert außerdem den später deployten Report-Consumer: Ein nicht persistierender öffentlicher GET auf Merge-Commit 08d55c3 lieferte den vollständigen optionalen Block für den versionierten Referenzpunkt. Ein fehlender Block bleibt bei fehlender Rasterabdeckung oder einem fail-closed Release-/Rechte-Gate weiterhin vertragskonform.

  • hitzetage_30c_jahr, extreme_hitzetage_35c_jahr, tropennaechte_20c_jahr und hitzewellentage_kysely_jahr sind historische mittlere Tage pro Jahr, nicht Werte eines aktuellen Kalenderjahres.
  • periodenbeginn ist 1991, periodenende ist 2020, source_vintage ist 1991-2020, dataset_version ist 1.
  • source_slug, source_release_id, release_key, transform_version, retrieved_at und quelle_stand binden den konkreten aktiven Release.
  • resolution_m: 1000, native_crs: EPSG:3416, easting_m und northing_m beschreiben die tatsächlich genutzte regionale Rasterzelle.
  • attribution, attribution_url, license: CC-BY-4.0, license_url: https://creativecommons.org/licenses/by/4.0/legalcode, terms_url und boundary sind untrennbare Pflichtprovenienz.

Fehlt eine Freigabe, ein Wert oder ein Herkunftsfeld, bleibt der gesamte Block unsichtbar; ältere Reports ohne diesen additiven Block bleiben gültig. Die Darstellung enthält keine Ampel, keine Prognose, keinen Trend und keine parzellenscharfe Messung. Der Klima-Block daneben bezeichnet weiterhin das abgeschlossene Referenzjahr 2025. Details: öffentlicher HORA-Hitzebelastungs-Vertrag.

6.3 Weitere optionale Blöcke (Feldübersicht, Stand 07.10.2026)

Diese Blöcke stehen im deployten Report-Typ (lib/report/types.ts), haben aber keine eigene Zeile in der Tabelle oben. Alle sind optional; ein fehlender Block ist kein negativer Befund, der Grund steht gegebenenfalls in meta.degraded.

  • poi_dichte (OpenStreetMap, Pilot-Mirror, Lage-Indikator ohne Restriktion): score (0–100, erreichte Kategorien von fünf), categories (nahversorger, bildung, gesundheit, gastro, gruenraum, je boolean), nearby_count, ampel (gruen|gelb|rot), ampel_begruendung, hinweis, quelle_stand, source (mirror). Eine Kategorie gilt als erreicht, wenn mindestens ein POI in rund 900 m Luftlinie liegt (siehe Abschnitt „Nahversorgungsdarstellung“ oben); ÖV wird separat über oev abgebildet.
  • zweitwohnsitz (Länder-Verordnung via RIS, Gemeinde-Ebene, keine Rechtsauskunft): gelistet, gemeinde, bezirk, bundesland, vo, vo_fundstelle, vo_url, ampel, ampel_begruendung, hinweis, quelle_stand. null, wenn die Gemeinde nicht bestimmbar ist.
  • brachflaechen_potenzial (KI-Potenzialkarte 2024 aus dem qualifizierten OGD-Snapshot, neutraler Hinweis, keine amtliche Brachflächen-Feststellung): status (potential_hint|multiple_potential_hints|no_hint), features[] (unveränderte Rohwerte je Fläche: source_feature_id, kg_raw, gnr_raw, gkz_raw, bl_kz_raw, ez_raw, brownfield_total, prediction_subclass_dop, build_area_m2, sealed_area_m2, seal_percent, source_area_m2, quality_warnings[]), hinweis, quelle_stand, layer_revision, source_release_id, attribution. Reine Grenzmehrdeutigkeit, fehlende Coverage sowie technische oder Release-Fehler ergeben null und einen Eintrag in meta.degraded.
  • denkmal.nachbarn_radius_m (number): der tatsächlich geprüfte Radius der Nachbarprüfung in Metern; Quelle für alle Anzeigetexte zu denkmal.nachbarn[].

Abgrenzung zum Standort-Check: Die Snapshot-Lanes widmung-wien, widmung-vorarlberg, hochwasser-at und wlv-gzp, die der kostenlose Standort-Check zusätzlich liest, werden vom Report-Assembler (lib/report/build.ts) nicht gelesen. Widmung und Hochwasser im Report bleiben Niederösterreich-Pfade; der Report-Contract ändert sich dadurch nicht (Stand 07.10.2026).


7. bestand — historisches Ziel, nicht im aktuellen Contract

Der deployte Report-Typ besitzt keinen Top-Level-Block bestand. Die früher hier beschriebene Kombination aus Microsoft Building Footprints, Copernicus- Versiegelung und Sentinel-2-NDVI wurde nicht als Report-Contract umgesetzt. Produktcode und API-Konsumenten dürfen diese Felder weder voraussetzen noch aus einem fehlenden Block fachliche Aussagen ableiten. Eine spätere Einführung benötigt einen eigenen Quellen-, Rechte-, Coverage- und Contract-Slice.


8. grundbuch — historisches Ziel, nicht im aktuellen Contract

Der deployte Report-Typ besitzt keinen Top-Level-Block grundbuch; weder Premium noch Planer erhalten ihn. Es gibt auch keinen aktuellen Report-Pfad, der JustizOnline-Auszüge, Eigentümerangaben oder einen original_r2_key liefert. ADR-0016 (nicht im öffentlichen Doku-Portal verfügbar) bleibt ein historisches Ziel- und Datenschutzdokument, kein Betriebsbeleg. Vor einer Einführung wären mindestens Rechtsgrundlage, Rollen, Klarnamengrenze, Provider-/AVV-Vertrag, Retention und ein eigener API-Contract zu entscheiden.


9. scenarios[] — bis zu sechs Nutzungsszenarien

Bei erfolgreichem Geocoding und Engine-Lauf liefert die aktuelle Engine die sechs konfigurierten Nutzungsklassen Wohnen, Gewerbe, PV-Anlage, Co-Working, Lager und Tourismus. Ohne Koordinate oder bei Enginefehlern bleibt die Liste leer. Ampel, Kennzahlen, Kosten und Fördertrigger sind deterministisch (ADR-0015). Die vorhandenen sechs Kostenmodelle werden unabhängig von der Ampelfarbe berechnet; ein rotes Nicht-PV-Szenario behält deshalb seine Kostenspanne. PV bleibt seit #562 ohne Kostenrechnung. Der optionale Fließtext wird erst nach der Engine hinzugefügt und ist nur für standard und premium zulässig.

FeldTypPflichtHerkunftBeschreibung
`scenario_id`stringjaengineStabiler Slug, z. B. `pv-freiflaeche`.
`nutzungsklasse`enum `wohnen`|`gewerbe`|`pv_anlage`|`co_working`|`lager`|`tourismus`jaengineNutzungsklasse (ADR-0015 Säule 3).
`titel`stringjaengineAnzeige-Titel (Konfig-Template, kein LLM).
`ampel`enum `gruen`|`gelb`|`rot`jaengineEignung: harte Ausschlusskriterien (rot) / weiche Faktoren (gelb/grün).
`ampel_faktoren`object[]jaengine`{ faktor, bewertung:"+/-/0", quelle_ref }` — nachvollziehbare Regel-Treffer (Widmung, Fläche, Altlast sowie die Risiko-Layer Hochwasser HQ30/HQ100, Denkmalschutz, strenge Schutzgebiete → je -, stufen grün auf gelb). `quelle_ref` ist ein rollenbasierter Herkunfts-Slug (z. B. `annahme`, `standard-anker`, `fachentscheid-YYYY-MM-DD`) und nennt nie eine Person; abweichende Slugs aus Alt-Snapshots werden beim Lesen auf diese Form normalisiert.
`ausschluss_grund`string | nullwenn `rot`engineHarte Mindestanforderung verletzt (z. B. Mindestfläche, Schutzgebiet).
`kennzahlen`objectoptionalengineSzenario-spezifische deterministische Kennzahlen. Neue PV-Semantik: ausschließlich `spez_ertrag_kwh_pro_kwp`, sofern vorhanden; siehe §1.5. Alle aus Engine, nie LLM.
`cost`object | nulljaengine`{ low, expected, high, unit:"EUR", bandwidth_pct }`; der Typ erlaubt null, PV liefert null (nicht berechnet); die übrigen Kostenmodelle liefern unabhängig von der Ampelfarbe eine Kostenspanne.
`cost_components`object[]jaengineExakt ADR-0015: `[{ label, amount, source }]` — `source` zitiert Tabelle/Faktor.
`methodology`stringjastaticExakt ADR-0015: „ÖNORM B 1802-1:2022 + Heft 3/2025 + BPI Q-aktuell".
`as_of_date`ISO-8601 datejaengineADR-0015 — Cost-Stichtag (Config-Stand).
`disclaimer`stringjastaticADR-0015 — „Grob-Kostenschätzung, ersetzt keine Detailkalkulation durch Sachverständigen/Planer".
`foerder_trigger`string[]jaengineIDs förderfähiger Bausteine → join auf `foerderungen[]` (z. B. `kpc-flaechenrecycling`, `kpc-waermepumpe`).
ruleset_unbestaetigtbooleanjaengineKennzeichnet sichtbar, dass die fachlichen Regeln noch nicht freigegeben sind.
`narrativ`stringoptional; nur standard/premiumllm1–2 Sätze. Der LLM-Input enthält nur nicht rote Nicht-PV-Szenarien; beim Ergebnis-Mapping fehlt derzeit ein zweiter Ampel-Guard. Fehlt bei free, planer, fehlendem Key oder jedem fail-open LLM-Fehler.

Offene Frage / Annahme: ADR-0015 listet Nutzungsklassen Wohnen/Gewerbe/PV/Co-Working/Lager/Tourismus, die exakten harten Mindestanforderungen + Cost-Bausteine pro Klasse liefert der POC- Woche-3-Workshop des Cofounders. Dieser Spec fixiert nur Struktur, nicht die Werte → Workshop-Input erforderlich.


10. foerderungen[] — deterministischer Konfigurationskatalog

Die aktuelle Engine liest den eingecheckten, fachlich noch unbestätigten Entwurfs-Förderkatalog und verbindet ihn deterministisch über scenarios[].foerder_trigger. Ein Förder-RAG, ein Klassifikations-LLM, match_score oder match_begruendung sind im deployten Report-Contract nicht vorhanden.

FeldTypPflichtHerkunftBeschreibung
`foerder_id`stringjaengineStabiler Slug, referenziert von scenarios[].foerder_trigger.
`programm`stringjastaticSichtbarer Programmname.
`traeger`stringjastaticTräger aus der unbestätigten Entwurfskonfiguration.
`max_betrag_eur`number | nulljastaticKonfigurationswert, nie vom LLM erzeugt.
`max_satz_pct`number | nulljastaticKonfigurationswert, nie vom LLM erzeugt.
`bedingung`stringjastaticSichtbare Bedingung aus der Konfiguration.
`quelle`stringjastaticQuellen-/Standhinweis des Katalogeintrags.

11. `sources[]` — Attribution-Pflicht (POC-Kriterium)

Pro tatsächlich verwendetem Layer genau ein Eintrag. Erfüllt das POC-Akzeptanzkriterium „Quellen-Block … Datenquelle + Lizenz + Stand" und die Attribution-Pflicht aus DATA-SOURCES §Attribution-Maintenance.

FeldTypPflichtHerkunftBeschreibung
`layer`stringjaengineStabile Report-Sektion, z. B. `parcel`, `widmung`, `klima` oder `altlasten`.
`quelle`stringjastaticSichtbarer Quellenname beziehungsweise vorgeschriebener Attributionstext.
`lizenz`stringjastaticSichtbare Lizenzbezeichnung, z. B. „CC BY 4.0".
`stand`stringjaengineFachlicher Quellenstand. Fehlt ein Vintage, muss ein technischer Abruf ausdrücklich als solcher etikettiert werden, z. B. Techn. Abruf 2026-08-26.
`source_release_id`UUIDoptionalengineExakter qualifizierter Release bei release-gebundenen Quellen. Für den FND-03-BEV-Snapshot bei `hit`, `no_hit` und `ambiguous_boundary` Pflicht.
`quelle_url`URLoptionalstaticÖffentliche Primärquelle; niemals interner Artefakt- oder Storage-Pfad.
`lizenz_url`URLoptionalstaticMaschinenlesbarer Lizenzlink für Web und PDF.
`aenderungshinweis`stringoptionalstatic/engineSichtbarer Hinweis auf LandNutzen-Normalisierung oder Ableitung. Beim FND-03-BEV-Snapshot Pflicht.
`non_endorsement_hinweis`stringoptionalstaticSichtbarer Hinweis, dass die Quelle die LandNutzen-Auswertung nicht bestätigt oder unterstützt. Beim FND-03-BEV-Snapshot Pflicht.
`modellhinweis`stringoptionalstaticSichtbare fachliche Grenze eines Modells oder einer Ableitung.

Für den qualifizierten BEV-Snapshot müssen Quelle, CC-BY-4.0-Lizenz, Primär- und Lizenzlink, fachlicher Stand, source_release_id, Änderungshinweis und Non-Endorsement sowohl im Web als auch im PDF sichtbar bleiben. Ein WMS-Legacy-Treffer darf keine Snapshot-Release-ID übernehmen.

Für den Altlasten-WFS müssen quelle exakt „Datenquelle: Umweltbundesamt", die bedingte UBA-Weiterverwendung, Primär-/Bestätigungslink, der als technisch etikettierte Abruf und source_release_id in Web und PDF sichtbar bleiben. Die UBA-Bestätigung ist keine OGD-/CC-Lizenz. Ein fehlender oder abgelaufener Pointer sowie Rechte-, Qualitäts-, Frische- oder Scope-Drift erzeugen meta.degraded und niemals einen grünen Altlasten-No-Hit.

sources[] enthält die im konkreten Report tatsächlich verwendeten Attributionen. Eine feste Mindestzahl und confidence.poc_min_layers_met gehören nicht zum aktuellen Runtime-Contract. Fehlende oder gesperrte Quellen werden über meta.degraded und den jeweils nullable/fehlenden Layer sichtbar, nicht über ein erfundenes Konfidenzobjekt.


12. confidence — historisches Ziel, nicht im aktuellen Contract

Der deployte Report-Typ besitzt keinen Top-Level-Block confidence und berechnet keine Gesamt-Konfidenz. Das ist beabsichtigt: Quellenausfälle und fachliche Grenzen bleiben layergenau in meta.degraded, Provenienzfeldern, Hinweisen und Kostenbandbreiten sichtbar. Ein einzelner Gesamtwert würde diese unterschiedlichen Grenzen unzulässig verdichten.


13. `disclaimer` — Haftung & Opt-in

FeldTypPflichtHerkunftBeschreibung
`disclaimer.text`stringjastaticVoller Haftungsausschluss-Text (ADR-0007/POC-Kriterium B).
`disclaimer.opt_in_required`booleanjastaticImmer `true` — Checkbox vor Report-Anzeige (POC-Kriterium B).
`disclaimer.opt_in_recorded_at`ISO-8601 datetime | nulljaengineWann der User die Checkbox bestätigt hat (Audit). `null` = noch nicht bestätigt → Report bleibt verdeckt.
`disclaimer.bausteine`objectjastaticQuellen-spezifische Pflicht-Hinweise. Vier Kernfelder sind immer vorhanden; optionale Felder werden nur bei vorhandenem Layer gesetzt, unter anderem für Radon, Breitband, Lärm, ÖV, POI-Dichte, Hochwasser, Schutzgebiete, Denkmal/Nachbarn, Zweitwohnsitz, WLV, Pendeln, Demografie, Boden, Bahn, Erdbeben, LCZ, Google Solar und Brachflächen-Potenzial. grundbuch_stichtag ist im Typ reserviert, bleibt mangels aktuellem grundbuch-Block aber ungesetzt.

14. Optionale Szenario-Narrative

Es gibt keinen Top-Level-Block narrative. Der einzige aktuelle LLM-Output ist das optionale scenarios[].narrativ aus §9. standard und premium verwenden dafür denselben standardReport-Task; free und planer rufen keinen LLM auf. Modell und Usage werden operativ im LLM-Usage-Ledger erfasst, nicht als narrative.modell in den Report geschrieben.

Es gibt außerdem weder narrative.summary, narrative.empfehlung noch narrative.guard_passed. Fehlt das optionale Narrativ, bleiben Engine-Felder und PDF vollständig. Die Promptgrenze „keine neuen Zahlen“ ist kein Ersatz für den nicht implementierten automatischen Zahlen-Gegencheck.


15. Aktuelle technische Tiermatrix

Es gibt noch keine organizations.tier-Spalte und kein deploytes Tier-/Billing-Modell. lib/report/tier.ts leitet Premium/Planer derzeit provisorisch aus aktiven institutionellen Organisationstypen ab; alle nicht explizit zugeordneten Fälle bleiben fail-safe free. standard existiert im technischen Contract, wird vom provisorischen Org-Mapping aber noch nicht vergeben. Frühere Preis-, Abo- oder „registriert = standard“-Aussagen sind keine aktuelle Produktzusage.

Contract-/Runtime-Aspektfreestandardpremiumplaner
Pflicht-Top-Level-Felder aus §2gleichgleichgleichgleich
sechs scenarios[] mit Engine-Zahlenbei erfolgreichem Engine-Laufbei erfolgreichem Engine-Laufbei erfolgreichem Engine-Laufbei erfolgreichem Engine-Lauf
scenarios[].narrativneinoptional; Input nur nicht rotoptional; Input nur nicht rotnein
solar.googleneinneinoptional bei API-Key/Coverageoptional bei API-Key/Coverage
Persistenz von solar.googleentfälltentfälltnein, Live-Overlay wird vor Speicherung entferntnein, Live-Overlay wird vor Speicherung entfernt
foerderungen[], sources[], disclaimergleicher Contractgleicher Contractgleicher Contractgleicher Contract
bestand, grundbuch, confidence, Top-Level-narrativenicht vorhandennicht vorhandennicht vorhandennicht vorhanden

Weitere optionale Layer hängen von Coverage, aktivem Quellenrelease, Rechten, Qualität und Providerverfügbarkeit ab, nicht pauschal vom Tier. Ein fehlender Layer darf nicht als negativer fachlicher Befund umgedeutet werden.

Die Report-Quota ist vom dargestellten Produkttier getrennt (Report-Quota-Vertrag):

  • anonym: 10 Anfragen/Tag pro pseudonymem Request-IP-Bucket;
  • bestehende, sicher aufgelöste Kontositzung: 10 Anfragen/Tag pro pseudonymem Account-Bucket — unabhängig von anderen Konten hinter derselben IP;
  • API-Key: das eigene in api_keys.daily_limit hinterlegte Tageslimit.

Ein angebotener ungültiger oder widerrufener API-Key bleibt 401 und darf nicht auf Sitzung oder anonym zurückfallen. Kann die bestehende Sitzung nicht sicher aufgelöst werden oder wurde der User gelöscht, gilt der anonyme IP-Bucket. X-Auth-Mode weist anon, session oder key aus; die beiden Rate-Limit-Header beschreiben den tatsächlich gewählten Bucket. Der Account-Bucket ersetzt nicht reports.ip_hash: Dort bleibt das Pseudonym der tatsächlichen Request-IP gespeichert.

Tier-Bestimmung erfolgt serverseitig aus Auth-Kontext (ADR-0006), nie client-seitig — Feld-Stripping passiert im Backend, bevor das JSON den Server verlässt. Die bestehende Browser-Sitzung eröffnet kein neues headless Cookie-Auth-Verfahren; Maschinenclients verwenden weiterhin API-Keys.


16. PDF-Mapping und PDF/UA-Shadow

Der öffentliche Produktpfad verwendet für alle vier Tiers @react-pdf/renderer und anschließend pdf-lib. Es gibt kein deploytes Puppeteer-Premium-Template, keine grundbuch-Sektion, keine angehängte HORA-Pass-Datei und kein narrative.guard_passed. Das PDF rendert die im gespeicherten Report tatsächlich vorhandenen strukturierten Felder sowie ein gegebenenfalls vorhandenes scenarios[].narrativ. Der bestehende Mehrseiten-Viewervertrag bleibt in REPORT-OUTPUT verbindlich; er ist kein PDF/UA-Nachweis.

ADR-0060 definiert getrennt den internen pdfkit@0.20.1-Shadow:

Tierfeste Narrativefester synthetischer Google-Solar-BlockOutputvertrag
freeneinnein5 Seiten, 2 Figuren, 2 Links
standardja, nur nicht rotnein5 Seiten, 2 Figuren, 2 Links
premiumja, nur nicht rotja5 Seiten, 2 Figuren, 2 Links
planerneinja5 Seiten, 2 Figuren, 2 Links

Dieser Shadow ist ausschließlich per POST /api/internal/pdf-ua-shadow/[tier] in Vercel Preview erreichbar. Die Route prüft Methode, URL, Query, Body-Rahmen und den Tier-Pfad, aber keine Request-Payload fließt in die PDF-Inhalte.

Slice C nach ADR-0061 speist nun vier repository-fixierte Objekte dieses deployten Report-Contracts ein. Der Mapper bindet meta, parcel, steckbrief, scenarios, foerderungen und den optionalen solar.google-Block an die vorhandene PDF/UA-View. Nicht freigegebene Felder — insbesondere Adresse, Koordinate und Provider-Rohfelder — werden nicht weitergereicht. Der Pfad liest weiterhin weder DB, Session, Provider noch fachliche Umgebungsdaten. Seine exakte Seiten-/Figuren-/Linkzahl ist ein synthetischer Testvertrag und keine Produktzusage. Produktion antwortet für die interne Route absichtlich 404; dies ist für alle vier Tier-POSTs und einen GET auf Merge-Commit 59036df7aca8a70025efcbf8f0d8dd467700cb9c und Produktionsdeployment dpl_FbKhtfArLz6wnDTAaRn3aq1bVJh9 rückgelesen. Der öffentliche React-PDF-Pfad bleibt unverändert. Issue #333 bleibt bis zur vollständigen Realreport-, Viewer-, Rollout-, Live- und Screenreader-Abnahme offen.

Slice D nach ADR-0062 ersetzt diese offene Realreportsektionen-Lücke im synthetischen Contract: Die Fixtures enthalten nun alle 31 repräsentativen Bereiche des deployten Report-Typs, und der Mapper muss sie exakt geordnet in Lage, Umwelt, Restriktionen und Governance überführen. Fehlende Bereiche oder abweichende relevante Kardinalitäten schlagen fail-closed fehl.

Der Outputvertrag wächst für alle vier Tiers auf acht Seiten, drei Figures, zwei Links und 16 Pagination-Artefakte. Die Figure-Alternativtexte werden aus Fläche und künstlicher Fixture-ID, vier Risikosektionen sowie den sechs Szenariokosten abgeleitet. Sie enthalten keine Adresse, Koordinate oder reale Karte. Die Preview-Route kennzeichnet den Stand als slice-d-1; Datenbank-, Session-, Provider- und Netzwerkgrenzen bleiben unverändert.

Diese Sektionsparität gilt nur für die repository-fixierten synthetischen Objekte. Echte gespeicherte Reports, der Mehrseiten-Viewer des neuen Renderers, Produktrollout, Live-Readback und Screenreader-Abnahme bleiben offen. Der öffentliche React-PDF-Pfad bleibt unverändert.

Der geschützte Slice-D-Preview dpl_Ew4nDGJZYUX9fRyiNSuqSz8FFutU bestand den Vier-Tier-Vertrag auf Commit 088ed8dab1749da623e354ffaa5bab48acc3cd17. Merge-Commit 056eafb0eddd0ec6369f71db65862f37b788b152 und Produktionsdeployment dpl_2bsKrqhys1CuRFQV6QUV6HiSJghg belegen die Produktionstrennung: Die interne Route antwortet dort für alle vier Tier-POSTs und einen GET leer 404. Das öffnet keinen PDF/UA-Produktpfad.

Slice E nach ADR-0063 schließt die synthetische Store-Lücke: free, standard, premium und planer werden über die exakten saveReport()- und getReport()-SQL-Abfragen in einem isolierten PostgreSQL-Testschema als echtes jsonb gespeichert und wieder als StoredReport gelesen. Der Renderer erhält ausschließlich dieses geladene Objekt. Der Test prüft Ablauf, Eigentümer, Disclaimer-Opt-in und den Ausschluss von solar.google, solar_google sowie dem zugehörigen Disclaimer-Baustein aus reports.report_json.

Der fail-closed Mapper übernimmt keine Report-ID, Adresse, Eigentümer-ID oder exakten Opt-in-Zeitpunkt. Die View PDF-UA-SLICE-E-1 exponiert nur minimierte Persistenzmerkmale. Der Preview-Shadow rendert eine repository-fixierte Kopie des persistenznormalisierten Kanaris und liest selbst keine Datenbank; erfolgreiche Antworten tragen slice-e-1. Acht Seiten, 31 Sektionen, drei Figures, zwei Links und 16 Pagination-Artefakte bleiben unverändert.

PR #541, CI-Lauf 33163812578 und der geschützte Preview dpl_AejPoE9E4Joeh9J1ireiHZdKz7k7 verifizieren alle vier Tiers. Der Merge-SHA f5d57d9ac0b4c57a576fa64b9fbddbff49ef8726 wird vom Produktionsdeployment dpl_GqMcWA5uHvML97ZrciT4dkzPKoQD geliefert; dort bleibt die interne Route für alle vier Tier-POSTs und einen GET leer 404.

Diese Prüfung verwendet keine Produktionsdatenbank und keinen gespeicherten Nutzerreport. Der öffentliche React-PDF-Pfad bleibt unverändert. Karten- und Viewerregression, Produktrollout, PDF/UA-Live-Readback sowie manuelle Screenreader-Abnahme bleiben offen; Issue #333 bleibt offen.

Slice F nach ADR-0064 schließt den automatisierbaren Apple-Rendererteil der Viewerregression. Auf macOS lädt PDFDocument die vier bereits vom bestehenden Harness erzeugten PDFs. Für jedes Tier sind acht Seiten, zwei Linkannotationen, acht seitenspezifische Textmarker und mindestens 100 Zeichen Textschicht pro Seite verpflichtend. Quartz rendert anschließend alle 32 Seiten in 8-Bit-Graustufe; dieselben Rastergrenzen wie bei Poppler verwerfen leere, abgeschnittene oder dunkel gefüllte Seiten.

Der lokale Referenzlauf auf macOS 26.6.2 und Swift 6.3.3 bestand 32/32 Seiten. Nicht-macOS-Läufe melden den fehlenden Apple-Stack ausdrücklich und behalten veraPDF sowie beide Poppler-Backends als CI-Vertrag. PR #543, CI-Lauf 33166935667 und Preview dpl_5NCcXGThbbpocRGCsfhS2VsZMg5H belegen auch die vier deployten Shadow-Dateien mit veraPDF und Apple PDFKit / Quartz. Merge-SHA e998d3e42886adef491eb489f5c8a874aca652a3 wird vom Produktionsdeployment dpl_D4fhMzmmwCDvhGrTcAN3Mm3TMcEL geliefert; dort bleibt die interne Route für vier Tier-POSTs und einen GET leer 404.

Slice F verändert weder slice-e-1 noch Route oder React-PDF-Produktpfad. Die manuelle Apple-Vorschau- und VoiceOver-Stichprobe wurde am 4. September 2026 für die SHA-256-gebundene synthetische Premium-Preview-Datei abgeschlossen: acht von acht Seiten waren vollständig sichtbar und per Tastatur erreichbar; Überschriften, Tabellen, Listen, drei beschriebene Abbildungen und zwei benannte Links wurden im nativen Accessibility-Baum exponiert.

Chrome/PDFium-Tag-Semantik, echte Karten, Produktrollout und PDF/UA-Live-Readback eines echten Nutzerreports bleiben offen; Issue #333 bleibt offen.


17. Historisches Beispiel-JSON — nicht als aktueller Contract verwenden

Das folgende Beispiel bleibt ausschließlich als Entscheidungshistorie des frühen Zielmodells erhalten. Es ist kein gültiges Beispiel des deployten Report-Typs: Unter anderem schema_version, die zweite Top-Level- report_id, bestand, grundbuch, confidence und narrative existieren heute nicht; mehrere meta- und Förderfelder weichen ebenfalls ab. Implementierung, Tests und Integrationen müssen stattdessen den Typ aus §2 beziehungsweise lib/report/types.ts verwenden.

```json { "schema_version": "1.0.0", "report_id": "9f2c1e7a-0b44-4d21-9c3e-7a1b6f0e2d55", "meta": { "address": { "raw": "Hauptstraße 12, 2230 Gänserndorf", "normalized": "Hauptstraße 12, 2230 Gänserndorf, Niederösterreich, AT" }, "coordinate": { "lat": 48.3417, "lon": 16.7192, "source": "bev" }, "region": { "slug": "at-noe", "name": "Niederösterreich" }, "report_tier": "premium", "org_id": 42, "requested_by": "user", "as_of": "2026-05-16", "generated_at": "2026-05-16T09:14:02Z", "engine_version": "0.3.0", "config_version": "bodenwerte@2025.4+herstellungskosten@heft3-2025+bpi@2025Q2", "degraded": [] }, "parcel": { "found": true, "lookup_status": "hit", "parcel_uid": "AT:08221:1247/3", "source_release_id": "cb735e6f-6c32-5de0-87fd-594aa34a1903", "match_method": "point_in_polygon", "match_confidence": 1, "grundstuecksnummer": "1247/3", "katastralgemeinde": "08221 Gänserndorf", "area_m2": 1840, "area_tolerance_pct": 10, "source": "dkm_mirror", "kataster": "grundsteuerkataster", "bev_stand": "2026-04-01", "vermessung_disclaimer": "Grundstücksgeometrie und -fläche stammen aus der BEV-Digitalen Katastralmappe (DKM) und ersetzen keine rechtsverbindliche Vermessung. Verbindliche Grenzen und Flächen ergeben sich nur aus Grundbuch bzw. behördlicher Vermessung (Grenzkataster)." }, "widmung": { "klasse": "bauland", "detail": "Bauland-Wohngebiet (BW)", "ampel": "gruen", "ampel_begruendung": "Bauland-Wohngebiet, baureif, keine harten Restriktionen", "restriktionen": [], "rechtsverbindlich": false, "quelle_stand": "2026-05-12" }, "risiko": { "hochwasser": { "stufe": "niedrig", "hq_zone": null }, "hora_pass": { "r2_key": "hora-pass/48.3417-16.7192.pdf", "gefahren": { "hochwasser": "niedrig", "oberflaechenabfluss": "niedrig", "lawine": "keine", "erdbeben": "mittel", "rutschung": "keine", "windspitzen": "mittel", "blitzdichte": "niedrig", "hagel": "mittel", "schneelast": "niedrig", "hitzeepisoden": "mittel" } }, "altlasten": { "intersect": false, "nearby_m": null, "referenz": null, "ubaid": null, "hinweis": "Nicht-Listung garantiert keine Schadstofffreiheit. Enthalten sind ausschließlich veröffentlichte Altlasten des Altlastenportal-WFS; der Status „Altlast vorgeschlagen“ ist ausgeschlossen. Punktprüfung, keine rechtsverbindliche Auskunft.", "quelle_stand": null, "source_release_id": "8ed8d136-f3f8-5a78-946b-4b419a050269", "retrieved_at": "2026-08-26T03:16:54.083Z", "source_vintage": null }, "geogen": { "rutschung": "keine" }, "klima": { "jahresmittel_temp_c": 11.9, "jahresniederschlag_mm": 467.7, "sonnenstunden_jahr": 2121.7, "referenzjahr": 2025, "quelle_stand": "2026-08-04", "source_slug": "geosphere-spartacus-v3", "source_release_id": "81ef8803-2d64-5d30-9d7f-cd526ce57e69", "release_key": "74f23173-4256-5296-b4e3-d93aa1f70a5c", "dataset_version": "3", "transform_version": "spartacus-v3-snapshot-v1", "source_vintage": "2025", "retrieved_at": "2026-08-04T23:16:02.000Z", "resolution_m": 1000, "native_crs": "EPSG:3416", "easting_m": 650500, "northing_m": 498500, "attribution": "Datenquelle: GeoSphere Austria - https://data.hub.geosphere.at", "attribution_url": "https://data.hub.geosphere.at", "license": "CC-BY-4.0", "license_url": "https://dataset.api.hub.geosphere.at/v1/docs/user-guide/license.html", "terms_url": "https://data.hub.geosphere.at/de/legal?lang=de", "boundary": "Das 1-km-Raster ist ein regionaler Klimaindikator und weder Messung noch Prognose für eine einzelne Parzelle." } }, "bestand": { "bebaut": true, "gebaeude_anzahl": 1, "footprint_flaeche_m2": 142, "versiegelungsgrad_pct": 31, "ndvi_index": 0.42, "footprint_stand": "2025-09-01" }, "grundbuch": { "bezirksgericht": "Gänserndorf", "katastralgemeinde": "08221 Gänserndorf", "einlagezahl": "612", "as_of": "2026-04-02T07:51:10Z", "eigentum": { "art": "Schlichtes Miteigentum", "personen": [ { "anteil": "1/2", "name": "[PSEUDONYMISIERT]", "wohnort_grob": "AT, NÖ" }, { "anteil": "1/2", "name": "[PSEUDONYMISIERT]", "wohnort_grob": "AT, Wien" } ], "aktivierungs_komplexitaet": "mittel" }, "belastungen": [ { "art": "Hypothek", "berechtigter": "[PSEUDONYMISIERT]", "impact": "lösbar mit Refinanzierung" } ], "dienstbarkeiten_zugunsten": [], "dienstbarkeiten_zulasten": [ { "art": "Wegerecht", "ueber": "GSt 1247/3", "zugunsten": "GSt 1247/4" } ], "score": { "aktivierungs_faehigkeit": "mittel", "punktwert": 1, "kommentar": "Zwei Miteigentümer, eine lösbare Hypothek. Aktivierung mit überschaubarem Koordinationsaufwand realistisch, Vorbereitung 3–6 Monate." }, "original_r2_key": "grundbuch/3f9a...c1.pdf", "disclaimer": "Stand: 2026-04-02. Spätere Änderungen können bestehen. Verbindliche Auskunft nur via aktuellem Auszug." }, "scenarios": [ { "scenario_id": "pv-freiflaeche", "nutzungsklasse": "pv_anlage", "titel": "PV-Freiflächen-Anlage 20 kWp", "ampel": "gelb", "ampel_faktoren": [], "ausschluss_grund": null, "kennzahlen": { "kwp": 20, "ertrag_kwh_jahr": 22800, "ertrag_eur_jahr": 6100, "strompreis_ct_kwh": 26.8 }, "cost": { "low": 26000, "expected": 30000, "high": 34500, "unit": "EUR", "bandwidth_pct": 20 }, "cost_components": [ { "label": "PV-Module + Wechselrichter (20 kWp)", "amount": 26000, "source": "Konfig: PV 1.300 EUR/kWp installiert (ADR-0015 Säule 3)" }, { "label": "Montage Freifläche + Unterkonstruktion", "amount": 3000, "source": "Konfig: Freiflächen-Aufschlag" }, { "label": "Netzanschluss + Zählertausch", "amount": 1000, "source": "Konfig: Standard-Netzanschluss NÖ" } ], "methodology": "ÖNORM B 1802-1:2022 + Heft 3/2025 + BPI Q-aktuell", "as_of_date": "2026-05-14", "disclaimer": "Grob-Kostenschätzung, ersetzt keine Detailkalkulation durch Sachverständigen/Planer", "foerder_trigger": ["kpc-pv"], "narrativ": "PVGIS liefert für die Beispielkonfiguration einen rechnerischen Jahresertrag von etwa 22.800 kWh. Daraus folgt keine Eignungsentscheidung; Verschattung, Dach- beziehungsweise Flächennutzung, Netzanschluss und Genehmigungen sind gesondert zu prüfen. Die Investitionskosten liegen bei etwa 30.000 EUR (±20 %)." }, { "scenario_id": "wohnen-sanierung", "nutzungsklasse": "wohnen", "titel": "Sanierung Bestandsgebäude für Wohnnutzung", "ampel": "gelb", "ampel_faktoren": [ { "faktor": "Widmung Bauland-Wohngebiet", "bewertung": "+", "quelle_ref": "noegis-widmung" }, { "faktor": "Bestand vorhanden", "bewertung": "+", "quelle_ref": "ms-footprints" }, { "faktor": "Erdbeben mittel (HORA)", "bewertung": "-", "quelle_ref": "hora-pass" } ], "ausschluss_grund": null, "kennzahlen": { "wohnnutzflaeche_m2": 110 }, "cost": { "low": 232000, "expected": 290000, "high": 348000, "unit": "EUR", "bandwidth_pct": 20 }, "cost_components": [ { "label": "Gebäude-Bestand-Sanierung", "amount": 255000, "source": "Heft 3/2025 NÖ gehoben 3.200 EUR/m² × 110 m² × 0.72 (Sanierungs-Faktor)" }, { "label": "Heizungstausch (KPC-förderfähig)", "amount": 25000, "source": "Konfig: HVAC-Wärmepumpe Standard" }, { "label": "Außenanlagen-Aufschlag", "amount": 10000, "source": "Heft 3/2025: ~2 % Aufschlag (einfach)" } ], "methodology": "ÖNORM B 1802-1:2022 + Heft 3/2025 + BPI Q-aktuell", "as_of_date": "2026-05-14", "disclaimer": "Grob-Kostenschätzung, ersetzt keine Detailkalkulation durch Sachverständigen/Planer", "foerder_trigger": ["kpc-waermepumpe", "kpc-flaechenrecycling"], "narrativ": "Das Bestandsgebäude lässt sich für Wohnnutzung sanieren. Der Sanierungsaufwand wird auf rund 290.000 EUR geschätzt (±20 %), basierend auf dem Heft-3/2025-Wert für NÖ gehobene Ausstattung und einem Sanierungs-Faktor. Der Heizungstausch ist KPC-förderfähig." }, { "scenario_id": "lager-logistik", "nutzungsklasse": "lager", "titel": "Lager-/Logistiknutzung", "ampel": "rot", "ampel_faktoren": [ { "faktor": "Widmung Wohngebiet schließt Lager aus", "bewertung": "-", "quelle_ref": "noegis-widmung" } ], "ausschluss_grund": "Harte Ausschlussregel: Widmung Bauland-Wohngebiet lässt gewerbliche Lagernutzung nicht zu.", "kennzahlen": {}, "cost": { "low": 0, "expected": 0, "high": 0, "unit": "EUR", "bandwidth_pct": 0 }, "cost_components": [], "methodology": "ÖNORM B 1802-1:2022 + Heft 3/2025 + BPI Q-aktuell", "as_of_date": "2026-05-14", "disclaimer": "Grob-Kostenschätzung, ersetzt keine Detailkalkulation durch Sachverständigen/Planer", "foerder_trigger": [], "narrativ": "Eine Lager-/Logistiknutzung ist an diesem Standort aufgrund der Widmung Bauland-Wohngebiet rechtlich ausgeschlossen." } ], "foerderungen": [ { "foerder_id": "kpc-pv", "programm": "KPC Photovoltaik", "traeger": "KPC", "max_betrag_eur": null, "max_satz_pct": 30, "bedingung": "Investitionszuschuss für Photovoltaik-Anlagen, Antrag vor Umsetzung", "match_score": 0.88, "match_begruendung": "Das PV-Freiflächen-Szenario fällt in den förderfähigen Anlagentyp der KPC-Photovoltaik-Richtlinie.", "quelle": "KPC-Richtlinie PV, Stand Q1/2026 (quartalsweise validiert)" }, { "foerder_id": "kpc-flaechenrecycling", "programm": "KPC Flächenrecycling", "traeger": "KPC", "max_betrag_eur": 60000, "max_satz_pct": 75, "bedingung": "Bis 75 % bzw. max. 60.000 EUR für Konzept, 50.000 EUR Detailuntersuchung", "match_score": 0.71, "match_begruendung": "Reaktivierung einer untergenutzten Baufläche — passt zum Flächenrecycling-Zweck.", "quelle": "KPC-Richtlinie Flächenrecycling, Stand Q1/2026" } ], "sources": [ { "layer": "parcel", "quelle": "Datenquelle: BEV – Bundesamt für Eich- und Vermessungswesen (data.bev.gv.at), CC BY 4.0", "lizenz": "CC BY 4.0", "stand": "2026-04-01", "source_release_id": "cb735e6f-6c32-5de0-87fd-594aa34a1903", "quelle_url": "https://data.bev.gv.at/geonetwork/srv/api/records/ac633f87-19f4-4f78-9052-74c63ff88331", "lizenz_url": "https://creativecommons.org/licenses/by/4.0/", "aenderungshinweis": "LandNutzen normalisiert BEV-KG und Grundstücksnummer zu einer stabilen internen Parzellen-ID und berechnet den Punkt-in-Polygon-Treffer.", "non_endorsement_hinweis": "Das BEV bestätigt oder unterstützt diese LandNutzen-Auswertung nicht." }, { "layer": "widmung", "quelle": "Land NÖ — Widmungsumhüllende (OGD, gespiegelt nach PostGIS)", "lizenz": "CC BY 4.0", "stand": "2026-05-12" }, { "layer": "klima", "quelle": "Datenquelle: GeoSphere Austria - https://data.hub.geosphere.at", "lizenz": "CC-BY-4.0", "stand": "2025", "source_release_id": "81ef8803-2d64-5d30-9d7f-cd526ce57e69", "quelle_url": "https://data.hub.geosphere.at/en/dataset/spartacus-v3-1y-1km", "lizenz_url": "https://dataset.api.hub.geosphere.at/v1/docs/user-guide/license.html", "aenderungshinweis": "LandNutzen hat die gelieferten Integer-Skalierungen ausschließlich für die Anzeige umgerechnet: TM durch 10 in °C, RR durch 10 in mm und SA durch 3600 in Stunden.", "non_endorsement_hinweis": "GeoSphere Austria bestätigt oder unterstützt diese LandNutzen-Auswertung nicht.", "modellhinweis": "Das 1-km-Raster ist ein regionaler Klimaindikator und weder Messung noch Prognose für eine einzelne Parzelle." }, { "layer": "altlasten", "quelle": "Datenquelle: Umweltbundesamt", "lizenz": "Weiterverwendung gemäß UBA-Bestätigung vom 11.08.2026", "stand": "Techn. Abruf 2026-08-26", "source_release_id": "8ed8d136-f3f8-5a78-946b-4b419a050269", "quelle_url": "https://www.altlasten.gv.at/atlas/altlasten-gis.html", "lizenz_url": "https://github.com/ostheimer/landnutzen/issues/351#issuecomment-5249657094", "aenderungshinweis": "LandNutzen beschränkt den WFS auf veröffentlichte Altlasten, schließt den Status „Altlast vorgeschlagen“ aus und prüft nur den Adresspunkt.", "modellhinweis": "Der technische Abrufzeitpunkt ist kein fachlicher Datenstand. Die Punktprüfung ist ein Screening und keine Altlastenfreiheitsbestätigung." } ], "confidence": { "overall": "hoch", "notes": ["SPARTACUS v3 · Datenstand 2025 · 1-km-Raster (regionaler Klimaindikator)"], "poc_min_layers_met": true, "cost_bandwidth_default_pct": 20 }, "disclaimer": { "text": "Dieser Report ist eine unverbindliche Erst­einschätzung des Nutzungs-Potenzials. Er ersetzt keine rechtsverbindliche Vermessung, keine Sachverständigen­bewertung und keine Detailkalkulation. Alle Werte sind Grob-Schätzungen mit angegebener Bandbreite.", "opt_in_required": true, "opt_in_recorded_at": "2026-05-16T09:13:55Z", "bausteine": { "widmung_nicht_rechtsverbindlich": "Widmungs­angaben sind nur Indikation, keine rechtsverbindliche Auskunft der Gemeinde.", "alsag_keine_freiheitsgarantie": "Nicht-Listung im Altlastenkataster garantiert keine Schadstofffreiheit.", "bev_keine_vermessung": "Errechnete Flächen/Szenarien ersetzen keine rechtsverbindliche Vermessung.", "cost_keine_sachverstaendigenbewertung": "Grob-Kostenschätzung nach ÖNORM B 1802-1, keine Sachverständigen­bewertung im Sinne der GewO.", "grundbuch_stichtag": "Grundbuch-Daten Stand 2026-04-02. Spätere Änderungen können bestehen." } }, "narrative": { "summary": "Die Liegenschaft in 2230 Gänserndorf liegt in Bauland-Wohngebiet ohne harte Restriktionen. Drei Nutzungsszenarien wurden geprüft: eine PV-Freiflächen-Anlage (empfohlen, ca. 6.100 EUR Jahresertrag), eine Wohn-Sanierung des Bestands (rund 290.000 EUR) sowie eine Lagernutzung (widmungsrechtlich ausgeschlossen). Die Eigentumsverhältnisse (zwei Miteigentümer) erlauben eine Aktivierung mit überschaubarem Koordinationsaufwand.", "empfehlung": "Kurzfristig größter Hebel: PV-Freiflächen-Anlage mit KPC-Photovoltaik-Förderung als niederschwelliger Einstieg, parallel Klärung der Hypothek für ein späteres Sanierungs­szenario.", "modell": "anthropic/claude-opus-4.7", "guard_passed": true } } ```


18. Offene Contract- und Fachentscheidungen

  • Harte Mindestanforderungen + Cost-Bausteine pro Nutzungsklasse (Wohnen/Gewerbe/PV/Co-Working/Lager/Tourismus) — dieser Spec fixiert nur die Struktur (ADR-0015 Säule 3, POC Woche 3).
  • Ampel-Schwellen: Welche weichen Faktoren kippen `gruen`→`gelb`, welche sind harte `rot`-Ausschlüsse je Klasse?
  • Tier-/Billing-Modell: organizations.tier, standard-Zuordnung, Preise und Produktrechte sind noch nicht implementiert; das provisorische Org-Type-Mapping ist keine Abrechnungszusage.
  • Narrativ-Zahlenschutz: Soll der im Prompt gesetzte Zahlenvertrag um einen tatsächlichen strukturellen Zahlen-Gegencheck und klaren Fail-closed-/Retry-Vertrag ergänzt werden?
  • Grundbuch: Wenn dieses historische Ziel wieder aufgenommen wird, müssen Rechtsgrundlage, Klarnamen-/Rollenmodell, Provider/AVV, Retention und ein neuer Contract gemeinsam entschieden werden.
  • Förder-Katalog-Pflege: Welche Programme im MVP-Katalog, wer validiert quartalsweise die Beträge/Sätze (DATA-SOURCES §Förder).
  • PV-Darstellung #562: Variante A; spezifischer Standortertrag ohne automatische Anlagen-/Eurohochrechnung. Fachliche Eignungsfreigabe bleibt #291.

Chrome/PDFium-Viewerregression (#333, Slice G)

Slice G ergänzt den echten Chrome/PDFium-Viewer für vier synthetische Tiers mit je acht Seiten. Der gepinnte Chrome for Testing 148.0.7778.96 prüft vollständige A4-Seiten, seitenspezifischen Text und benannte Links. Darstellung und Tag-Semantik sind getrennte Gates: Im Standardviewer fehlen Tabellen-, Listen- und Bildrollen. Ein grüner Darstellungscheck ist deshalb keine vollständige Chrome-Barrierefreiheitsabnahme.

Der öffentliche React-PDF-Pfad bleibt unverändert, der Shadow bleibt slice-e-1 und Preview-only. Chrome-Tag-Semantik, echte Karteninhalte, kontrollierter Produktrollout und PDF/UA-Live-Readback eines echten Nutzerreports bleiben offen; Issue #333 bleibt offen. Vertrag und verifizierter Release-Stand: Slice-G-Entscheidung und Slice-G-Nachweis.

Slice G wurde über PR #548 produktiv ausgeliefert: Merge 8ccc9fe28236c894bee69de5502ff11b98c592c0, Deployment dpl_Y8GsNSbTzn5swXMfVsxU7Zt5QRiw, Main-CI 33914599076. Lokal, im Linux-CI und mit den deployten Preview-Dateien bestanden 32/32 Chrome/PDFium-Seiten; sechs Dokuportalseiten und die geschlossene Shadow-Produktionsgrenze wurden live rückgelesen. Die Chrome-Tag-Semantik bleibt ausdrücklich nicht abgenommen. Der Slice-G-Nachweis enthält die commitgebundenen Prüfergebnisse.

PDF/UA-Parzellengeometrie (#550, Slice H)

Slice H ersetzt die feste Grundstücksskizze im geschützten synthetischen Shadow durch validierte Polygon-/MultiPolygon-Vektoren. Der optionale interne Eingang bindet den bestehenden pdf_parcel_outline-Export an den Reporthash und Public-/Export-Release. Rechte, Coverage, Frische und Consumerbindung bleiben Voraussetzung. Fehlende oder abgelehnte Geometrie erzeugt einen Absatz statt einer Figure; Innenringe werden sichtbar als nicht unterstützt abgelehnt. Alt-Text, hervorgehobener Treffer, Nachbarn, Nordrichtung sowie Herkunft und Lizenz folgen dem tatsächlichen Eingang.

Der positive Slice-H-Previewbeleg bleibt ausdrücklich synthetisch. Der damalige fehlende echte Geometriebeleg wird im getrennten Slice I weitergeführt; dessen aktueller Abnahmestand steht im verlinkten Slice-I-Nachweis. Chrome-Tag-Semantik, weitere Karten, Produktrollout und echter PDF/UA- Nutzerreport bleiben offen; #333 bleibt offen. Der öffentliche Renderer bleibt React-PDF. Slice-H-Vertrag und Slice-H-Nachweis trennen lokale Implementierung, CI/Preview, Produktion und Live-Readback.

PDF/UA: echte interne Parzellenreferenz (#553, Slice I)

Slice I ergänzt einen festen internen QA-Einstieg für den vorhandenen anonymen Schul-Referenzreport aus dem DXF-Produktionsnachweis. Ein enger lesender Selektor, getReport, die atomaren pdf_parcel_outline-Gates und wiederholte Report-/ Quellenprüfung binden genau eine Zielparzelle an die finalisierte PDF/UA-Figure. Es gibt keine neue HTTP-Route und keine frei wählbaren Report- oder Geometrieeingaben.

Der eigene QA-Mapper bewahrt die echte Reportidentität. Zielparzelle, Schuladresse, gespeicherte DKM-Fläche und BEV-Herkunft sind real; alle übrigen Prüfbestandteile bleiben sichtbar synthetisch. Die vorhandene Kartenadapter-Vereinfachung von 23 Originalstützpunkten auf neun Koordinaten ist von der unveränderten Auswahl und metrischen Projektion getrennt mit Hashes belegt. Innenringe der Zielparzelle, Mehrdeutigkeit sowie negative Rechte-/Release-/Coverage-/Quality-/Freshness-/ Quarantänegates bleiben gesperrt. Das H-Chrome-Profil 20/7/6/3/2 bleibt erhalten; I hat ein eigenes semantisches 20/7/6/3/4-Profil.

Die echte interne Referenz ist auf Merge 5609b182db810983e0240eacbab5cfce2eb9ac5a mit aktueller Produktionsquelle lesend geprüft. PR #554, der gebaute Preview und zwölf schreibfreie Produktionschecks sind bestanden. Die vollständige Main-CI 33953550809 ist grün. READY-Deployment dpl_FDEorR7WyuagkPvSw281WnREWSXB und getrennte CLI-/HTTP-Belege stehen im Slice-I-Nachweis. Die reale I-PDF wurde intern erzeugt; die HTTP-Prüfung bestätigt Dokumentation und Produktionssperre des synthetischen H-Shadows. ADR-0067 dokumentiert Vertrag und Abnahmetests. Chrome-Tag-Semantik, weitere echte Karten, öffentlicher PDF/UA-Rollout und echter PDF/UA-Nutzerreport bleiben offen; #333 bleibt offen. Öffentlicher React-PDF-Pfad und synthetischer Preview bleiben erhalten. Es wurde kein neuer Report angelegt.