Report-Spezifikation — JSON-Contract

Dieses Dokument definiert den vollständigen Report-JSON-Contract von LandNutzen. Ziel: ein Entwickler kann danach die deterministische Szenario-/Cost-Engine, den LLM-Prompt und die beiden PDF-Templates (React-PDF Standard, Puppeteer Premium) bauen, ohne raten zu müssen.

Querverweise: PRODUCT.md · ARCHITECTURE.md · ADR-0015 Kostenschätzung · ADR-0013 PDF · ADR-0006 Auth/Multitenant · ADR-0016 Grundbuch · ADR-0014 HORA-Pass · DATA-SOURCES.md · POC-PLAN.md


1. Grundprinzipien

1.1 Trennung Engine ↔ LLM (verbindlich)

Konsistent mit ADR-0015:

``` [Deterministische Engine berechnet ALLE Zahlen + Ampeln + Scores] ↓ [strukturiertes Report-JSON (dieser Contract)] ↓ [LLM erhält Report-JSON + Quellenliste + Schreib-Anweisung] ↓ [LLM rendert NUR narrative Felder, zitiert Engine-Werte 1:1] ↓ [Zahlen-Konsistenz-Guard: Regex auf €/kWh/m² → Abgleich mit JSON] ```

Der LLM erfindet, rechnet oder rundet keine Zahlen. Er befüllt ausschließlich Felder, die unten als Herkunft `llm` markiert sind, und darf darin keine Zahl nennen, die nicht 1:1 aus einem `engine`-Feld stammt. Bei Diskrepanz: Auto-Retry mit härterer Anweisung, sonst Fallback auf Template-Render (statischer Satzbaustein).

1.2 Herkunfts-Klassen pro Feld

HerkunftBedeutung
`engine`Deterministische Regel-/Geo-/Cost-Engine. Reproduzierbar bei gleichem Input. Nie LLM.
`llm`LLM-narrativer Text (Sonnet 4.6 Standard / Opus 4.7 Premium). Keine neuen Zahlen.
`static`Konstante aus Konfig/Recht (Disclaimer-Boilerplate, Lizenz-Strings, Tarif-Mapping).
`external`1:1 von externer Quelle übernommen (BEV-Geometrie-Ref, HORA-Pass-PDF-Key).

1.3 Determinismus & Caching

Gleicher `(parcel_id, region, report_tier, engine_version, config_version)` → identische `engine`/`external`-Felder. `llm`-Felder dürfen variieren (Temperatur > 0), müssen aber den Zahlen-Guard bestehen. `meta.as_of` und `meta.generated_at` machen jeden Report stichtagsfähig.


2. Top-Level-Schema

```jsonc { "schema_version": "1.0.0", // SemVer dieses Contracts; static "report_id": "uuid", // engine "meta": { ... }, // §3 "parcel": { ... }, // §4 "steckbrief": [ { ... } ], // Ampel-Übersicht aller Indikatoren (Aggregat; engine) "standort_bilanz": { ... } | null,// Risiko-Rollup aus dem Steckbrief (Szenario-Kontext; engine) "widmung": { ... }, // §5 "risiko": { ... }, // §6 "bestand": { ... }, // §7 "grundbuch": { ... } | null, // §8 — nur Premium, sonst null "scenarios": [ { ... } ], // §9 — 3..5 Elemente "foerderungen": [ { ... } ], // §10 "sources": [ { ... } ], // §11 — Attribution-Pflicht "confidence": { ... }, // §12 "disclaimer": { ... }, // §13 "narrative": { ... } // §14 — die einzigen rein-llm-Blöcke } ```

Pflicht-Top-Level-Keys für alle Tiers: `schema_version`, `report_id`, `meta`, `parcel`, `widmung`, `risiko`, `bestand`, `scenarios`, `foerderungen`, `sources`, `confidence`, `disclaimer`, `narrative`. `grundbuch` ist immer vorhanden, aber `null` außerhalb des Premium-Pfads (siehe §8 und §15).

`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 abgeleitet (intersect → rot, Nähe < 1 km → gelb, sonst grün).

`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.


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

FeldTypPflichtHerkunftBeschreibung
`meta.address.raw`stringjaexternalVom User eingegebene Adresse (roh). Wird NICHT an LLM gegeben (PII-Minimierung, ARCHITECTURE §Compliance).
`meta.address.normalized`stringjaengineNormalisierte Adresse (Geocoder-Output).
`meta.coordinate.lat`numberjaengineWGS84, Geocoding (Nominatim/BEV).
`meta.coordinate.lon`numberjaengineWGS84.
`meta.coordinate.source`enum `nominatim`|`bev`jaengineGeocoder.
`meta.region.slug`stringjaenginez. B. `at-noe` (ADR-0006 Region-Achse).
`meta.region.name`stringjastatic„Niederösterreich".
`meta.report_tier`enum `free`|`standard`|`premium`|`planer`jaengineSteuert Sektions-Sichtbarkeit (§15). `free`=anonym, `standard`=registriert, `premium`=490-€-Beratung, `planer`=Abo-Export.
`meta.org_id`int | nulljaengineOrg-Kontext (RLS, ADR-0006). `null` bei anonym (`_public`-Org).
`meta.requested_by`enum `anonymous`|`user`|`platform_admin_impersonating`jaengineAudit-Hinweis (ADR-0006/0007).
`meta.as_of`ISO-8601 datejaengineFachlicher Daten-Stichtag (max. Alter aller verwendeten Layer).
`meta.generated_at`ISO-8601 datetimejaengineGenerierungs-Zeitpunkt.
`meta.engine_version`stringjastaticVersion der Szenario-/Cost-Engine.
`meta.config_version`stringjastaticVersion `bodenwerte.json` + `herstellungskosten.json` + BPI-Stand.
`meta.degraded`object[]jaengineListe ausgefallener Layer mit Defensive-Default-Hinweis (ARCHITECTURE §Failure-Modi). Leer wenn alles ok.
`meta.timings`object[]optionalengineDiagnostische Layer-Latenzen (Wall-Clock je OGD-Abruf), absteigend sortiert (Flaschenhals zuerst). Nicht deterministisch — kein Teil der fachlichen Aussage, dient dem Erkennen langsamer Endpunkte.
`meta.report_id`stringoptionalenginePermalink-ID, falls der Report persistiert wurde (nur POST, ADR-0022). Fehlt bei fail-open-Persistenzfehler.
`meta.permalink`stringoptionalengineTeilbarer Pfad `/r/{report_id}`, falls persistiert.
`meta.saved`booloptionalengine`true`, wenn der Report einem Konto fest zugeordnet ist (eingeloggt erstellt oder per `?claim=1` beansprucht, Migration 0021) → kein 90-Tage-Ablauf. Fehlend/`false` = anonym (90-Tage-Retention) oder nicht persistiert. Steuert „gespeichert"-/Registrierungs-Nudge im UI.

`meta.degraded[]`-Element: `{ "layer": "geosphere", "reason": "rate_limit", "fallback": "static_aggregate" }`. `meta.timings[]`-Element: `{ "layer": "parcel", "ms": 812 }` (gemessen über die in build.ts konkurrent gestarteten Layer-Calls; der langsamste ≈ Gesamt-Wall-Clock).


4. `parcel` — BEV-Kataster

Quelle: BEV-WFS Live-Call (ARCHITECTURE §Datenfluss 4a), 30-Tage-Cache.

FeldTypPflichtHerkunftBeschreibung
`parcel.found`booleanjaengineOb Adresse einer Parzelle zugeordnet werden konnte. Wenn `false` → nur Teilreport, Banner.
`parcel.grundstuecksnummer`stringwenn `found`externalBEV-Grundstücksnummer.
`parcel.katastralgemeinde`stringwenn `found`externalKG-Name + Nummer.
`parcel.geometry_ref`stringwenn `found`externalReferenz auf gespiegeltes PostGIS-Polygon (keine Roh-Geometrie im JSON; Export lizenzrechtlich kritisch, DATA-SOURCES §BEV).
`parcel.area_m2`numberwenn `found`engineAus Geometrie berechnete Fläche.
`parcel.area_tolerance_pct`numberwenn `found`staticToleranzmarge (DKM ≠ Grundbuch), Default 3 %.
`parcel.grundstuecksnutzung`stringoptionalexternalBEV-Nutzungsart (Bauf./Gärten/…).
`parcel.bev_stand`ISO-8601 datewenn `found`externalStand des BEV-Datensatzes (Cache-Datum).

5. `widmung` — Flächenwidmung als Ampel

Quelle: NÖGIS Widmung, wöchentlich gespiegelt, `ST_Intersects`.

FeldTypPflichtHerkunftBeschreibung
`widmung.klasse`enum `bauland`|`gruenland`|`industrie`|`sonder`|`gemischt`|`unbekannt`jaengineAggregierte Hauptklasse.
`widmung.detail`stringoptionalexternalOriginal-Widmungstext NÖGIS.
`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.restriktionen`string[]jaengineHarte Einschränkungen aus Zusatz-Layern (NÖGIS Wasserrecht Schutz-/Schongebiet, Lärminfo-Klasse). Leer = keine.
`widmung.rechtsverbindlich`booleanjastaticImmer `false` — nur Indikation, keine rechtsverbindliche Auskunft (DATA-SOURCES §NÖGIS).
`widmung.quelle_stand`ISO-8601 datejaengineStand der Widmung (Mirror-Sync bzw. LAND_DATUM aus dem Live-Layer).
`widmung.source`enum `dkm_mirror`|`wms`optionalengineHerkunft: dkm_mirror = exakte gespiegelte Geometrie (PostGIS); wms = NÖ-weiter Live-Fallback über RRU_WI_HUELLE (WMS-GetFeatureInfo), greift bei Mirror-Lücke/DB-Ausfall — gleiche Klassifikation, keine exakte Fläche.

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, WMS-GFI). null = keine kartierte Zone.
`risiko.hochwasser.jaehrlichkeiten`number[]jaengineAlle am Punkt vorhandenen Jährlichkeiten, aufsteigend (z. B. [30,100,300]).
`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.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live WMS-GFI gegen sdi.noe.gv.at).
`risiko.hora_pass.r2_key`string | nulloptionalexternalR2-Key des HORA-Pass-PDF (ADR-0014). `null` wenn Layer aus = Lizenz nicht geklärt.
`risiko.hora_pass.gefahren`objectjaexternalMap der 10 HORA-Gefahren → Stufe (`keine`/`niedrig`/`mittel`/`hoch`): `hochwasser`, `oberflaechenabfluss`, `lawine`, `erdbeben`, `rutschung`, `windspitzen`, `blitzdichte`, `hagel`, `schneelast`, `hitzeepisoden`. Aus Pfad-A-Parse; leeres Objekt wenn Pass nicht abrufbar.
`risiko.altlasten.intersect`booleanjaengine`ST_Intersects` BEV-Parzelle × ALSAG-Polygone.
`risiko.altlasten.flaeche`string | nullwenn `intersect`externalBezeichnung der publizierten Altlast/Sanierungsfläche.
`risiko.altlasten.hinweis`stringjastaticFix: „Nicht-Listung garantiert keine Schadstofffreiheit." (DATA-SOURCES §ALSAG, R-03).
`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.geogen.rutschung`enum `keine`|`vorbegutachtung`|`erkundung`|`unbekannt`jaengineNÖGIS Geogene Gefahren `ST_Intersects` (Hanglagen).
`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). `null` bei PVGIS-Ausfall → PV-Szenario nutzt die Konfig-Pauschale.
`solar.spez_ertrag_kwh_pro_kwp`numberjaexternalPVGIS spez. Jahresertrag [kWh/kWp] bei optimaler Ausrichtung (AC); Engine-Override der Pauschale `1140`.
`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)externalDachgenaue Google-Anreicherung (Building Insights). DC-Werte, Live-Overlay — vor Persistenz entfernt (`stripGoogleSolar`, ToS R-07); auf Permalinks abwesend. `null` = keine Coverage / Free-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`.
`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 | nulljaengineKlassen-Freitext der Quelle (z. B. „≥ 1 Gbit/s").
`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`).
`laerm`object | nulloptionalengineUmgebungslärm-Indikator (strategische Lärmkarten END 2002/49/EG, laerminfo.at). `null` nur bei API-Ausfall (→ `degraded.laerm`); „keine Zone" ist ein gültiges Ergebnis (Ampel grün). Nur Hauptquellen Straße + Schiene oberhalb der EU-Meldeschwellen, kein lokaler Lärm, berechnet nicht gemessen. Live via WMS-GetFeatureInfo.
`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.max_lden_db`number | nulljaengineHöchste Lden-Untergrenze über Straße + Schiene (Headline); `null` = keine Zone.
`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`stringjastaticWortgleich mit `disclaimer.bausteine.laerm_indikator`.
`laerm.quelle_stand`ISO-8601 datejaengineAbrufdatum (Live WMS-GFI gegen `inspire.lfrz.gv.at`).
`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 sind Magnus-Review-Punkt (Briefing).
`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.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 → rot; sonst grün.
`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; sonst Treffer → gelb; keine Zone → grün.
`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).
`risiko.klima.jahresmittel_temp_c`numberjaengineGeoSphere Jahresmittel-Temperatur (Redis-Cache pro 1×1-km-Zelle / Live).
`risiko.klima.jahresniederschlag_mm`numberjaengineGeoSphere Jahressumme Niederschlag.
`risiko.klima.sonnenstunden_jahr`numberjaengineGeoSphere/Strahlung — Basis PV-Szenario.
`risiko.klima.referenzjahr`intjaengineAggregations-Jahr (z. B. 2024).
`risiko.klima.quelle_stand`ISO-8601 datejaengineCache-/Sync-Datum.

7. `bestand` — Gebäude-Footprint

Quelle: Microsoft Building Footprints (Bulk, statisch) + optional Copernicus Versiegelung / Sentinel-2 NDVI (Phase 2).

FeldTypPflichtHerkunftBeschreibung
`bestand.bebaut`booleanjaengineOb ein Footprint die Parzelle schneidet.
`bestand.gebaeude_anzahl`intjaengineAnzahl geschnittener Footprints.
`bestand.footprint_flaeche_m2`numberjaengineSumme Footprint-Fläche auf der Parzelle.
`bestand.versiegelungsgrad_pct`number | nulloptionalengineCopernicus (Phase 2), sonst `null`.
`bestand.ndvi_index`number | nulloptionalengineSentinel-2 „überwuchert?" (Phase 2), sonst `null`.
`bestand.footprint_stand`ISO-8601 datejaengineDatensatz-Stand (statischer Bulk).

8. `grundbuch` — nur Premium (ADR-0016)

`null` für `free`/`standard`. Nur befüllt im `premium`-Pfad nach explizitem User-Trigger („Eigentumsverhältnisse abfragen") oder durch Magnus' Beratungsvorbereitung. Alle Personen pseudonymisiert — Klarnamen nie im Report-JSON, nie an LLM (ADR-0016 §Pseudonymisierung).

FeldTypPflichtHerkunftBeschreibung
`grundbuch.bezirksgericht`stringjaexternalBG aus JustizOnline-Auszug.
`grundbuch.katastralgemeinde`stringjaexternalKG-Code + Name.
`grundbuch.einlagezahl`stringjaexternalEZ.
`grundbuch.as_of`ISO-8601 datetimejaexternalStichtag des Auszugs (max. 90 Tage Cache, ADR-0016).
`grundbuch.eigentum.art`stringjaexternalz. B. „Schlichtes Miteigentum".
`grundbuch.eigentum.personen[]`object[]jaengine`{ anteil, name:"[PSEUDONYMISIERT]", wohnort_grob }` — `wohnort_grob` nur Land/Bundesland-Granularität.
`grundbuch.eigentum.aktivierungs_komplexitaet`enum `gering`|`mittel`|`hoch`jaengineAus B-Blatt abgeleitet.
`grundbuch.belastungen[]`object[]jaengine`{ art, berechtigter:"[PSEUDONYMISIERT]", impact }` — C-Blatt.
`grundbuch.dienstbarkeiten_zugunsten[]`object[]jaexternalC-Blatt, GSt-Referenzen (keine Personen).
`grundbuch.dienstbarkeiten_zulasten[]`object[]jaexternalC-Blatt.
`grundbuch.score.aktivierungs_faehigkeit`enum `einfach`|`mittel`|`schwierig`|`sehr_schwierig`jaengineDeterministische Score-Tabelle (ADR-0016, Magnus-kalibrierte Gewichte). Kein LLM.
`grundbuch.score.punktwert`numberjaengineRoh-Score (Bandbreiten: +5..+3 einfach … <−3 sehr_schwierig).
`grundbuch.score.kommentar`stringjaengineRegelbasierter Kurztext (z. B. realistischer Vorbereitungszeitraum).
`grundbuch.original_r2_key`stringjaexternalR2-Key des Original-PDF (Zugriff nur Auftraggeber, separater Auth-Check).
`grundbuch.disclaimer`stringjastatic„Stand: {as_of}. Spätere Änderungen können bestehen. Verbindliche Auskunft nur via aktuellem Auszug."

Offene Frage / Annahme: ADR-0016 fragt, ob eine eigene Rolle „Auftraggeber-mit-Namenseinsicht" nötig ist. Annahme dieses Specs: Klarnamen werden NIE in den Report-JSON-Contract aufgenommen; ein separater, nicht hier spezifizierter Endpoint liefert das Original-PDF an den authentifizierten Auftraggeber. Der Report bleibt damit durchgehend pseudonym → für Magnus' Workshop zu bestätigen.


9. `scenarios[]` — 3 bis 5 Nutzungsszenarien

Mindestens 3, maximal 5 Elemente (PRODUCT §User-Flow Punkt 3). Die Ampel-Bewertung und der gesamte `cost`-Block sind deterministisch (ADR-0015). Der `cost`-Block übernimmt das Schema aus ADR-0015 exakt und unverändert (`scenario`, `cost`, `cost_components`, `methodology`, `as_of_date`, `disclaimer`).

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).
`ausschluss_grund`string | nullwenn `rot`engineHarte Mindestanforderung verletzt (z. B. Mindestfläche, Schutzgebiet).
`kennzahlen`objectoptionalengineSzenario-spezifische deterministische Kennzahlen. PV-Beispiel: `{ "kwp": 20, "ertrag_kwh_jahr": 22800, "ertrag_eur_jahr": 6100, "strompreis_ct_kwh": 26.8 }`. Alle aus Engine, nie LLM.
`cost`objectjaengineExakt ADR-0015-Schema: `{ low, expected, high, unit:"EUR", bandwidth_pct }`.
`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`).
`narrativ`stringnur `standard`+llm1–3 Absätze Erklärung. Darf nur Zahlen aus `cost`/`kennzahlen` 1:1 zitieren. Im `free`-Tier weggelassen oder gekürzt (§15).

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


10. `foerderungen[]` — RAG-Matching

Quelle: Förder-RAG (KPC/LEADER/KMU.DIGITAL/aws-Richtlinien als PDFs in pgvector). Das Matching (welche Förderung passt) ist `classification`-LLM (gpt-4o-mini/gemini-flash) mit striktem Tool-Output — Beträge/Sätze sind NICHT generiert, sondern aus dem kuratierten Förder-Katalog (Konfig) gezogen.

FeldTypPflichtHerkunftBeschreibung
`foerder_id`stringjaengineStabiler Slug, referenziert von `scenarios[].foerder_trigger`.
`programm`stringjastaticz. B. „KPC Flächenrecycling".
`traeger`stringjastaticKPC / LEADER / KMU.DIGITAL / aws.
`max_betrag_eur`number | nulljastaticAus Förder-Katalog (z. B. 60000). Nie LLM.
`max_satz_pct`number | nulljastaticz. B. 75.
`bedingung`stringjastaticFörderbedingung aus Katalog.
`match_score`numberjaengineRAG-Relevanz 0..1.
`match_begruendung`stringnur `standard`+llmWarum passend — nur qualitativ, keine neuen Zahlen.
`quelle`stringjastaticRichtlinien-Dokument + Stand (quartalsweise validiert).

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
`source_id`stringjastaticz. B. `bev`, `noegis-widmung`, `alsag`, `geosphere`, `ms-footprints`, `hora-pass`, `grundbuch`.
`name`stringjastaticAnzeigename.
`lizenz`stringjastaticz. B. „CC BY 4.0", „ODbL", „Standardnutzungsrechte".
`attribution_text`stringjastaticExakt anzuzeigender String (z. B. „Kartendaten © basemap.at, CC BY 4.0").
`stand`ISO-8601 datejaengineLetzter erfolgreicher Sync/Abruf dieses Layers.
`verwendet_fuer`string[]jaengineWelche Report-Sektionen den Layer nutzen.

Ein Report muss ≥ 4 `sources` (BEV, NÖGIS-Widmung, GeoSphere, ALSAG) enthalten, sonst gilt das POC-Mindest-Datenlayer-Kriterium als verfehlt → `confidence.poc_min_layers_met = false`.


12. `confidence` — Unsicherheitshinweise

FeldTypPflichtHerkunftBeschreibung
`confidence.overall`enum `hoch`|`mittel`|`niedrig`jaengineAggregierte Konfidenz (fehlende Layer / Cache-Alter / `degraded`).
`confidence.notes`string[]jaengineKonkrete Hinweise (z. B. „Klima-Aggregat 5 Tage alt", „HORA-Layer aus — Lizenz nicht geklärt").
`confidence.poc_min_layers_met`booleanjaengine≥ 4 Pflicht-Datenlayer sichtbar verknüpft (POC-Kriterium A).
`confidence.cost_bandwidth_default_pct`numberjastaticStandard-Unsicherheit Cost (±20 %, ADR-0015 Säule 1).

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: `widmung_nicht_rechtsverbindlich`, `alsag_keine_freiheitsgarantie`, `bev_keine_vermessung`, `cost_keine_sachverstaendigenbewertung`, `radon_indikator_nicht_messung` (nur wenn `risiko.radon` gesetzt), `breitband_indikator` (nur wenn `breitband` gesetzt), `laerm_indikator` (nur wenn `laerm` gesetzt), `oev_indikator` (nur wenn `oev` gesetzt), `hochwasser_indikator` (nur wenn `risiko.hochwasser` gesetzt), `schutzgebiete_indikator` (nur wenn `schutzgebiete` gesetzt), `denkmal_indikator` (nur wenn `denkmal` gesetzt), `wlv_indikator` (nur wenn `wlv` gesetzt), `grundbuch_stichtag` (nur Premium).

14. `narrative` — die einzigen reinen LLM-Blöcke

FeldTypPflichtHerkunftBeschreibung
`narrative.summary`stringnur `standard`+llmExecutive-Summary (3–5 Sätze). Keine Zahl, die nicht in `engine`-Feldern steht.
`narrative.empfehlung`stringnur `premium`/`planer`llmHandlungsempfehlung (Opus 4.7).
`narrative.modell`stringjaengineWelches Modell den Narrativ erzeugt hat (`anthropic/claude-sonnet-4.6` etc.). Audit/Cost.
`narrative.guard_passed`booleanjaengineZahlen-Konsistenz-Guard bestanden (ADR-0015). `false` → Fallback-Template wurde gerendert.

Im `free`-Tier ist `narrative.summary` ein gekürzter Template-Satz (kein LLM-Call → Cost-Cap, POC LLM-Sub-Ziel < $0,10/Report), `narrative.modell = "template"`.


15. Free/anonym vs. Premium 490 € — Sektions-Matrix

Sektion`free` (anonym)`standard` (registriert)`premium` (490 €)`planer` (Abo)
`meta`, `parcel`, `widmung`
`risiko` (Klima/ALSAG/Hochwasser-Stufe)✅ Kurz✅ Voll✅ Voll✅ Voll
`risiko.hora_pass.r2_key` (PDF-Beilage)
`bestand`✅ Basis✅ + Versiegelung/NDVI✅ + Versiegelung/NDVI
`scenarios[]`3, ohne `narrativ`3–5 + `narrativ`3–5 + `narrativ` + Empfehlung3–5, tabellarisch
`scenarios[].cost_components`nur Summe✅ Detail✅ Detail✅ Detail
`foerderungen[]`Top 1, ohne Begründung✅ + Begründung✅ + Begründung
`grundbuch``null``null`✅ (nach Trigger)`null` (es sei denn explizit)
`narrative.summary`TemplateLLM (Sonnet)LLM (Opus)LLM (Sonnet)
`narrative.empfehlung`✅ (Opus)
`sources`, `confidence`, `disclaimer`

Free-Tier zusätzlich: Rate-Limit 10 Reports/Tag/IP (POC-Kriterium B). Tier-Bestimmung erfolgt server-seitig aus Auth-Kontext (ADR-0006), nie client-seitig — Feld-Stripping passiert im Backend, bevor das JSON den Server verlässt.


16. PDF-Mapping (ADR-0013)

TierPDF-PfadToolSektionen im PDF
`free` / `standard`Standard-Report-PDF`@react-pdf/renderer``meta`, Karte (pre-rendered PNG), `parcel`, `widmung`, `risiko` (kompakt), `bestand`, `scenarios` (Karten-Layout, Cost-Summe), Top-`foerderungen`, `sources`, `disclaimer`. Map als `(parcel_id, zoom, layer_preset)`-PNG-Cache.
`planer`Planer-Export-PDF`@react-pdf/renderer`Wie Standard, aber tabellen-lastig, alle `cost_components`, weniger Narrativ, hohe Volumina.
`premium` (490 €)Beratungs-Report-PDFPuppeteer (Fluid Compute)Alle Sektionen inkl. `grundbuch`, `narrative.empfehlung`, volle `cost_components`, interaktiv gerenderte MapLibre-Karte. HORA-Pass-PDF (`risiko.hora_pass.r2_key`) als letzte 2 Seiten angehängt (ADR-0014 Variante α — Attribution via BMLUK-Footer automatisch erfüllt).

Felder mit Herkunft `external`/`engine` werden in beide Templates 1:1 übernommen; `llm`-Felder nur wenn `narrative.guard_passed = true`, sonst Template-Fallback.


17. Vollständiges Beispiel-JSON (fiktive NÖ-Adresse, `premium`)

Fiktive Liegenschaft, Pilotregion NÖ. Enthält das POC-PV-Szenario mit konkreter Euro-Zahl.

```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, "grundstuecksnummer": "1247/3", "katastralgemeinde": "08221 Gänserndorf", "geometry_ref": "postgis:parcels/at-noe/08221/1247-3", "area_m2": 1840, "area_tolerance_pct": 3, "grundstuecksnutzung": "Baufläche begrünt", "bev_stand": "2026-04-28" }, "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, "flaeche": null, "hinweis": "Nicht-Listung garantiert keine Schadstofffreiheit." }, "geogen": { "rutschung": "keine" }, "klima": { "jahresmittel_temp_c": 10.4, "jahresniederschlag_mm": 548, "sonnenstunden_jahr": 1995, "referenzjahr": 2024, "quelle_stand": "2026-05-11" } }, "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": "gruen", "ampel_faktoren": [ { "faktor": "Sonnenstunden", "bewertung": "+", "quelle_ref": "geosphere" }, { "faktor": "Keine Beschattung Bestand", "bewertung": "+", "quelle_ref": "ms-footprints" }, { "faktor": "Widmung Bauland", "bewertung": "0", "quelle_ref": "noegis-widmung" } ], "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": "Die Parzelle eignet sich mit rund 1.995 Sonnenstunden im Jahr gut für eine PV-Freiflächen-Anlage. Eine 20-kWp-Anlage liefert rechnerisch etwa 22.800 kWh pro Jahr, was bei aktuellem Strompreis rund 6.100 EUR jährlichem Ertrag entspricht. 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": [ { "source_id": "bev", "name": "BEV Kataster / INSPIRE", "lizenz": "Standardnutzungsrechte", "attribution_text": "Katasterdaten © BEV", "stand": "2026-04-28", "verwendet_fuer": ["parcel"] }, { "source_id": "noegis-widmung", "name": "NÖGIS Flächenwidmung", "lizenz": "CC BY 4.0", "attribution_text": "Widmungsdaten © Land NÖ (NÖGIS), CC BY 4.0", "stand": "2026-05-12", "verwendet_fuer": ["widmung", "scenarios"] }, { "source_id": "geosphere", "name": "GeoSphere Austria", "lizenz": "CC BY 4.0", "attribution_text": "Klimadaten © GeoSphere Austria, CC BY 4.0", "stand": "2026-05-11", "verwendet_fuer": ["risiko"] }, { "source_id": "alsag", "name": "ALSAG / Altlastenportal", "lizenz": "OGD", "attribution_text": "Altlastendaten © Umweltbundesamt", "stand": "2026-05-01", "verwendet_fuer": ["risiko"] }, { "source_id": "ms-footprints", "name": "Microsoft Building Footprints", "lizenz": "ODbL", "attribution_text": "Gebäudedaten © Microsoft, ODbL", "stand": "2025-09-01", "verwendet_fuer": ["bestand", "scenarios"] }, { "source_id": "hora-pass", "name": "HORA-Pass (BMLUK)", "lizenz": "Information für die Öffentlichkeit", "attribution_text": "Naturgefahren-Indikation: HORA-Pass, hora.gv.at, BMLUK", "stand": "2026-05-15", "verwendet_fuer": ["risiko"] }, { "source_id": "grundbuch", "name": "Grundbuch (JustizOnline)", "lizenz": "Bundesabgabenordnung", "attribution_text": "Grundbuchauszug via JustizOnline, Stichtag 2026-04-02", "stand": "2026-04-02", "verwendet_fuer": ["grundbuch"] } ], "confidence": { "overall": "hoch", "notes": ["Klima-Aggregat 5 Tage alt (innerhalb Cache-Toleranz)"], "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 Fragen für Magnus' Domain-Workshop

  • 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?
  • Grundbuch-Klarnamen-Auth: Bestätigung der Annahme in §8 — Klarnamen nie im Report-JSON, separater Auftraggeber-Endpoint (ADR-0016 offene Frage).
  • Förder-Katalog-Pflege: Welche Programme im MVP-Katalog, wer validiert quartalsweise die Beträge/Sätze (DATA-SOURCES §Förder).
  • PV-Referenz-Annahmen: kWp→kWh-Faktor und Strompreis für die POC-Euro-Zahl — Engine-Konstante oder regionsabhängig?