Report-Output: Permalink & PDF
Nahversorgung: Luftlinien-Näherung (#564)
Der Hinweis beschreibt 900 m Luftlinie als Näherung für 15 Minuten zu Fuß. Es wird keine Route berechnet; Umwege und Barrieren werden nicht berücksichtigt. Web, Steckbrief und PDF benennen den Radius ausdrücklich. Bekannte historische Hinweis-/Ampeltexte werden ausschließlich für die Darstellung projiziert; Snapshot, Score, Kategorien, Quellenstand und Ampel bleiben unverändert. Unbekannte Radiusverträge erhalten neutrale Kurztexte; historische Kategorien werden aus dem gespeicherten Bestand angezeigt. Vertrag und Prüfnachweise trennen Implementierung, CI/Preview, unabhängige Abnahme und Produktions-Readback. #463 bleibt unverändert; PDF/UA #333, PV #291 und NÖGIS #413 bleiben eigene Gates.
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.
Jeder über POST /api/v1/report erzeugte Report wird persistiert
und ist teilbar — als Web-Permalink und als PDF
(ADR-0022).
Vierfach verfügbar (AI-Parität, ADR-0018): UI, API, MCP, Skill.
Permalink
Der Reportkopf ordnet Titel und Adresse auf schmalen Bildschirmen über
„PDF herunterladen“ an. Lange Adressen dürfen auch innerhalb eines Wortes
umbrechen. Ab 768 px steht die PDF-Aktion rechts daneben. Das mindestens
44 px hohe Ziel ist per Touch und Tab/Enter erreichbar; der Tastaturfokus
ist sichtbar. Der PDF-Endpunkt und sein Inline-Downloadverhalten bleiben
unverändert. Die Browserregression prüft 320, 390 und 1440 px auf dem
gebauten App-Pfad mit einer synthetischen gespeicherten Reportantwort
(npm run test:e2e:permalink:build). Der Testprozess ersetzt ausschließlich
die Neon-Antwort für den synthetischen Testhost; er legt keine Reports an.
POST /api/v1/report ergänzt im Ergebnis:
"meta": { "report_id": "…", "permalink": "/r/…" }
/r/{id}— öffentliche, gerenderte Ansicht (Marken-Layout, PDF-Button).noindex(kein Suchmaschinen-Index).- ID = CSPRNG ≥128 Bit base62 → nicht erratbar (IDOR-Schutz, ADR-0023).
- Retention 90 Tage (
expires_at) für anonyme Reports, danach 404 + Hard-Delete (täglicher Cron). Nurip_hashgespeichert, kein Klar-IP. - Konto-gebundene Reports (eingeloggt erstellt oder per
/r/{id}?claim=1beansprucht, Migration 0021):expires_at = NULL→ laufen nicht ab, abrufbar unter „Meine Reports" (/account/reports). Zusätzlichuser_idgespeichert;meta.saved=truesignalisiert den Zustand an UI/API. - Fail-open: scheitert die Persistenz, kommt der Report ohne
report_id(Kernfunktion unberührt).
Endpunkte
| Methode | Pfad | Ergebnis |
|---|---|---|
POST | /api/v1/report | Report persistiert + meta.permalink |
GET | /api/v1/report/{id} | gespeichertes REPORT-SPEC-JSON (404 wenn unbekannt/abgelaufen) |
GET | /api/v1/report/{id}/pdf | Hauptreport (application/pdf) |
GET | /api/v1/report/{id}/sources/pdf | Optionaler Quellenanhang desselben Snapshots; gemeinsames PDF-Tageslimit |
GET | /api/v1/report/{id}/dxf | bedingter, release-gebundener DXF-Export (siehe API-Leitfaden); 404, wenn nicht freigeschaltet |
GET | /r/{id} | gerenderte Web-Ansicht + PDF-Download |
GET /api/v1/report?address=… (Query) bleibt flüchtig
(idempotent, schreibt nicht).
Server-seitig via @react-pdf/renderer (reines JS, kein
Headless-Chromium) — robust/kalt-startarm auf Vercel. Bewusst
nüchternes Layout; Zahlen 1:1 aus dem gespeicherten Report.
Hauptreport und Quellenanhang teilen den pdf:-Bucket. Der optionale
„Quellenanhang (PDF)“-Download ist neben dem Hauptreport im frischen Ergebnis
und im Permalink erreichbar, sofern das Speichern erfolgreich war.
Quellennummern sind in beiden Dokumenten identisch und folgen der gespeicherten
Quellenliste nach den bestehenden Provenienzprüfungen. Der Anhang nennt
Report-ID, ursprünglichen Erstellzeitpunkt, Berichtsstand, Quellen-/Lizenzlinks,
gespeicherte Releases und vorhandene Abrufzeitpunkte. Er löst keine neue
Standort- oder Quellenabfrage aus. Erforderliche Attributionen, Einschränkungen,
Datenlücken und Entwurfsstatus bleiben im Hauptreport. Fehlt eine Report-ID,
gibt es keinen Download; unbekannte oder abgelaufene IDs liefern 404.
Beide öffentlichen PDFs sind weiterhin nicht als PDF/UA-konform ausgewiesen.
Die Szenario-Vergleichstabelle kann über mehrere Seiten fließen (#751). Überlange ausgewählte Prüfpunkte werden ohne Kürzung in höchstens 650 Unicode-Zeichen und acht explizite Zeilenumbrüche lange Fragmente geteilt, möglichst an einem Leerraum. Jedes Fragment hält Nutzung, Status und Prüfpunkt in denselben Spalten zusammen; Fortsetzungen nennen die Nutzung erneut und zeigen den Spaltenkopf. Kurze Tabellen (höchstens acht Szenarien, zusammen höchstens 650 Prüfpunkt- und 250 Titelzeichen sowie acht explizite Prüfpunkt-Zeilenumbrüche) behalten den bisherigen kompakten Tabellenrahmen. Lange Tabellen brauchen mehr Platz, verändern aber weder den Snapshot noch den ausgewählten Faktor. „Details“ bleibt mit dem ersten Szenariokopf verbunden. Bei überlangen Faktoren fließen die Szenario-Angaben und Faktoren in eigenen, beschrifteten Kartenfortsetzungen. Auch eine zugleich überlange gespeicherte PV-Quellenbezeichnung wird fragmentiert; es gibt keinen unteilbaren großen Basiskartenblock. Überlange Einzelwörter erhalten lokale Trennmöglichkeiten. Normale kurze Karten behalten den bisherigen Aufbau.
npm run qa:pdf:comparison prüft normale, sehr lange, wortlange, stark
mehrzeilige und paarweise lange synthetische Fälle sowie eine zugleich über
A4 lange PV-Quellenbezeichnung ohne Koordinate oder Netzwerk: unverändertes JSON,
vollständige Texte samt Endmarkierungen, Poppler-Glyphengeometrie ohne
Kopf-/Fuß- oder Textüberlappung, keine leeren Body-Seiten und Raster aller Seiten.
Die Raster werden zusätzlich visuell geprüft; der lokale Lauf ist kein
Produktions- oder PDF/UA-Nachweis.
Die gezielte Poppler-Renderer-Gegenprobe läuft zusätzlich im üblichen
Unit-Testpfad (pdf-comparison-render.test.ts); sie scheitert auf Main vor #751.
Darstellung in Web und PDF (Änderungen seit 4. Oktober 2026)
Web-Report und PDF lesen dieselben Funktionen und denselben gespeicherten Report; die Übersetzung geschieht beim Anzeigen, gespeicherte Reports und die JSON-API bleiben unverändert.
- Gefundene Adresse (ADR-0078, 05.10.2026): Das Deckblatt trägt unter der
eingegebenen Adresse die Zeile „Gefunden: …“ samt Hinweis, wenn der Treffer
außerhalb Niederösterreichs liegt oder die Eingabe mehrdeutig war. Wortlaut
und Logik:
lib/report/found-address.ts. Reports vor dem 05.10.2026 haben keinmeta.address.resolved; die Zeile wird für sie ausmeta.address.normalizedverdichtet. - Parzellen-Umriss und Quarantäne (#655, #661): Der Umriss wird nur am
betroffenen Punkt gesperrt (siehe REPORT-SPEC §4). Zeichnet das PDF den
Umriss, nennt es eine schneidende Quarantänehülle als Relativsatz: „In diesem
Ausschnitt fehlt möglicherweise eine Parzelle, deren Amtsgeometrie (BEV)
fehlerhaft ist und die deshalb nicht dargestellt wird.“ (bei mehreren „bis zu
N Parzellen“). Gemeinsame Funktion für Karte, PDF und DXF:
lib/report/parcel-status.ts. - Report-Lücken in Nutzersprache (#661): „Noch nicht im Report“ (Web) und der
Satz im PDF-Anhang zeigen Bezeichnungen und Gründe wie „Hochwasser-Gefahrenzonen
(HORA)“ oder „Klimaprofil“ statt interner Schlüssel, ADR-Nummern und
Dateinamen (
lib/report/degraded-display.ts).meta.degradedselbst bleibt der maschinenlesbare Schlüssel-Contract. - ÖV-Stichtage (#645): Der Hinweis
oev.stichtag_hinweisnennt den Fahrplan-Stichtag der Pilotlieferung und eine mögliche Abweichung zum Stichtag des Standort-Checks, wortgleich in Web und PDF. - Breitband-Klasse (#643, #648): Die Legendenklasse wird aus
breitband.max_download_mbitabgeleitet, auch beim Lesen gespeicherter Reports; Web und PDF nennen den Wert darunter. - PVGIS (#661): Fällt die Standortertrag-Abfrage vorübergehend aus
(Netzwerkfehler, HTTP 5xx), folgt genau ein Wiederholversuch innerhalb des
10-s-Budgets (REPORT-SPEC §6, Zeile
solar); sonst fehltsolarund die Lücke steht inmeta.degraded.
Verbindlicher Mehrseiten-Viewervertrag (#332)
Der PDF-Output ist erst viewerfest abgenommen, wenn npm run qa:pdf:poppler
über scripts/verify-pdf-poppler.ts denselben produktiven
renderReportPdf-Pfad für zwei feste, netzunabhängige semantische QA-Profile
prüft. Beide Fixtures haben coordinate: null sowie feste Reportzeit- und
Quellwerte, damit weder Karten- noch Provider-Netzaufrufe in den Nachweis
gelangen:
| Profil | Vertrag |
|---|---|
free | Gespeicherter Free-Report mit mindestens zwei physischen Seiten; keine nur für Premium vorgesehenen Inhalte. |
premium | Gespeicherter Premium-Report mit mindestens zwei physischen Seiten und Premium-Inhalten. |
Eine feste Seitenzahl gehört bewusst nicht zum Vertrag: neue Reportbausteine dürfen den Umfang verändern. Die Prüfung muss aber für beide Profile tatsächlich mehrseitig bleiben und jede physische Seite einbeziehen. Das Harness pinnt deshalb die aktuelle semantische Seitenzuordnung fail-closed auf Free 3 und Premium 4: Ändert ein beabsichtigter Layout- oder Inhaltsumbau den Umfang, müssen die seitenspezifischen Body-Marker im selben Diff bewusst nachgezogen werden. Diese Fixture-Map ist eine Regressionserwartung, keine dauerhafte Produktzusage über exakt 3 beziehungsweise 4 Seiten.
Automatisches Poppler-Gate
Für beide Profile gelten in Linux-/CI-Umgebungen dieselben Pflichten:
- Das finale PDF wird mit
pdftoppm -grayund mitpdftocairo -gray -pngvollständig gerendert. Erfolg nur bei Exit-Code 0, identischer Seitenzahl zum PDF und genau einer nichtleeren Rasterdatei pro physischer Seite und Renderer.pdfinfo,pdftotextundpdfinfo -urlprüfen Struktur, Text und Linkziele unabhängig vom Bildrendering. - Jeder Seitenhintergrund bleibt deckend weiß. Schwarze oder transparente
Flächen im geprüften Außenrand, ein zu hoher Dunkelanteil, schwarze
Ganzseiten und das frühere alternierende Schwarzbild sind Fehler. Eine
unerwartet inhaltsleere weiße Seite ist ebenfalls ein Fehler; „weißer
Hintergrund“ bedeutet nicht „leere Seite“. Zusätzlich muss der um Kopf,
Fuß und Seitenrand beschnittene Body-Crop pro Backend mindestens
0,150 %sichtbar dunkle Pixel mit einer Helligkeit von höchstens 220 enthalten. - Die Textschicht bleibt selektier- und extrahierbar. Der extrahierte Text
enthält pro physischer Seite mehrere seitenspezifische Body-Marker sowie die
lückenlose Folge
Seite X / Y. Die Raster- und Textgates wirken gemeinsam: unsichtbare Textobjekte können sichtbaren Body-Inhalt nicht ersetzen. - Mindestens der Report-Permalink bleibt als echte URI-Linkannotation erhalten. Weitere im Fixture enthaltene Quellen- und Lizenzlinks dürfen nicht zu reinem Bildtext degradiert werden.
- Die PDF-Metadaten bleiben maschinenlesbar: Titel mit Standort,
LandNutzenals Autor, Betreff, Schlüsselwörter, Sprachede-AT, Creator und Producer. Die vom Parser gemeldete Seitenzahl stimmt mit beiden Renderern und den Seitenzahlen im Dokument überein.
Das Gate prüft den fertigen Output nach React-PDF-Layout und PDF-Finalisierung. Ein erfolgreicher Komponenten-Snapshot oder eine Prüfung nur der ersten Seite genügt nicht.
Manueller Viewer-Check
Vor Abschluss einer Änderung am Seiten-Chrome werden Free- und Premium-PDFs mit demselben Renderer und denselben festen Fixture-Eingaben wie im automatischen Gate zusätzlich vollständig in beiden Viewern geprüft. Das Gate selbst hält keine Reportinhalte als Artefakt zurück; die für den manuellen Check neu erzeugten Dateien werden deshalb über ihren SHA-256 eindeutig protokolliert:
- Chromium mit PDFium: jede Seite sichtbar, weißer Hintergrund, konsistenter Kopf/Fuß, Textauswahl, anklickbare Links und lückenlose Seitenzahlen.
- Apple Vorschau: dieselben Sichtprüfungen sowie Titel, Autor und Betreff in den Dokumentinformationen.
Das Protokoll nennt Datum, macOS-/Chromium-/Viewer-Version, Profil, Seitenzahl und SHA-256 des geprüften PDFs. Eine Sichtprüfung in nur einem Viewer oder nur eines Profils erfüllt den Vertrag nicht.
Temporäre QA-Artefakte und Abschlussnachweis
Generierte PDFs, Rasterseiten und extrahierte Textdateien sind temporäre
QA-Artefakte. Das Harness erstellt dafür ausschließlich ein validiertes,
eindeutiges mkdtemp-Verzeichnis, räumt es auch im Fehlerfall im finally auf
und verwendet für die Rasteranalyse das gemeinsame Modul
scripts/lib/pdf-poppler-regression.mjs. Die Artefakte werden nicht
eingecheckt und enthalten keine echten Kunden- oder Kontodaten. Der Job
pdf_poppler in npm run check:local verlangt lokal installiertes Poppler
(fehlt es, meldet der Lauf den Job mit Grund als übersprungen) und führt
denselben npm run qa:pdf:poppler-Befehl aus; der Workflow ci.yml (GitHub-CI)
ist seit 05.10.2026 absichtlich ausgeschaltet. Die temporären
Reportinhalte werden nicht als Job-Artefakt veröffentlicht.
Die technische Abnahmematrix für Issue
#332 ist auf
Produktionscommit 734f22f3b17de1593da72ef997f7f1eac5e38e50 erfüllt. Free und Premium
bestanden beide Poppler-Renderer in PR- und Main-CI; PDFium, Chromium und Apple
Vorschau wurden über alle jeweiligen Seiten geprüft. Der zwölfseitige
Live-Readback bestand Text-, Link-, Metadaten-, Seitenzahl- und Rasterprüfung
auf demselben Commit. Evidenz-PR
#531 bindet die
Issue-Schließung mit Closes #332 atomar an seinen Merge und bestätigt das
ergänzte Body-Ink-/Semantikgate im separaten Poppler-CI-Lauf 32944989218.
Versionen, SHA-256-Werte der konkret geprüften Dateien, Seitenzahlen,
Deployment und Datenschutzgrenze stehen im
Produktionsnachweis.
PDF/UA-1-Machbarkeitsgate (#333, Slice A)
Der Viewer-Vertrag aus #332 belegt Darstellung, Textschicht, Links, Metadaten und Seitenzahlen, aber noch keinen semantischen Tag-Baum und keine PDF/UA-Konformität. Issue #333 bleibt deshalb getrennt offen. ADR-0059 definiert als ersten Slice ausschließlich ein synthetisches, nicht produktives PDF/UA-1-Machbarkeits- und Vertragsprüfgate:
- Eine feste, per SHA-256 gebundene und ohne ausführbare Inhalte netzwerkfreie HTML-Fixture mit erfundenen Daten deckt Überschriften, Absätze, Liste, Tabelle, Link, informative Abbildung mit Alternativtext sowie dekorative und wiederkehrende Artefakte ab.
- Ein Chromium-basierter Kandidatenpfad muss daraus einen semantisch
getaggten PDF-1.7-Output mit Sprache,
MarkInfo/Marked, PDF/UA-1-XMP, Dokumenttitel und -metadaten, Outline, Strukturbaum in der festgelegten Fixture-Lesereihenfolge, seitenweise gebundener physischer Textschicht, exaktem Linkziel und genau einer informativen Figure mit Alternativtext erzeugen. Ein gepinnter veraPDF-Lauf muss ohne fehlgeschlagene PDF/UA-1-Regel enden; die Textmarkerprüfung behauptet keine Zuordnung einzelner Texte zu konkreten MCIDs. - Eine semantisch entsprechende negative React-PDF-Gegenprobe muss am selben Prüfgate scheitern. Ein Prüfgate, das Kandidat und ungetaggten Bestandsoutput gleichermaßen akzeptiert, ist nicht trennscharf und damit rot.
Der lokale Validatornachweis dieses Slices ist ausgeführt: Der Kandidat bestand
106 Regeln und 4.679 Checks, die React-PDF-Gegenprobe scheiterte
erwartungsgemäß an 9 Regeln und 189 Checks. Linux-CI-Lauf 32951907614
bestätigte denselben Vertrag mit 106 Regeln und 4.645 Checks sowie einer
trennscharfen Gegenprobe; die Run-Artefaktliste blieb leer. Slice A ist damit
auf dem Harness-Commit dbd46f3c3a94 commit- und CI-gebunden abgeschlossen.
Der vollständige Lauf steht im
Machbarkeitsnachweis.
Der Produktionsrenderer bleibt unverändert
@react-pdf/renderer; es gibt noch keinen produktiven PDF/UA-Report und keine
Screenreader-Abnahme. Ein späterer Rendererwechsel benötigt getrennte
Free-/Standard-/Premium-/Planer-Integration, Runtime- und Viewergates,
kontrolliertes Rollout,
Live-Readback und eine manuelle Screenreader-Prüfung.
PDF/UA-1-Preview-Shadow (#333, Slice B)
ADR-0060 setzt den nächsten Schritt bewusst
nicht als Produktumschaltung, sondern als geschützten Vercel-Preview-Shadow um.
Der Shadow rendert mit pdfkit@0.20.1 in reinem Node.js. Stock
@sparticuz/chromium wurde verworfen, weil dessen unveränderter Build keinen
belastbaren Tagged-PDF-Pfad bereitstellt; Full Chrome aus Slice A ist kein
Serverless-Betriebsbeleg.
Der interne Vertrag lautet:
- ausschließlich
POST /api/internal/pdf-ua-shadow/[tier]mitfree,standard,premiumoderplaner; - nur bei
VERCEL_ENV=preview; Produktion, Development, die unterstützten Nicht-POST-Methoden, unbekannte Tiers, Queryparameter und Request-Body antworten leer mit404; andere framework- oder proxyseitig behandelte Methoden können bereits vor dem Handler einen abweichenden4xxliefern; - Vercel Deployment Protection ist die äußere Schranke; Slice B führt keinen neuen anwendungsseitigen Token ein;
- die interne Route liegt außerhalb des Auth.js-Proxy-Matchers, liest keine App-Sitzung und setzt keine Auth.js-CSRF-/Callback-Cookies;
- feste synthetische Fixture ohne DB, Session, Report-Persistenz, Provider, PII, request-abgeleitete Inhalte oder Netzwerkzugriff;
- pro Tier exakt fünf Seiten, zwei informative Figuren mit Alternativtext und zwei fest erlaubte HTTPS-Links;
- Figure-ID und Alt-Text-Digest bleiben positionsfest an die Roh-PDF gebunden; ein vertauschtes Manifest wird abgewiesen;
- kooperativer 50-Sekunden-Response-Timer und 4.000.000-Byte-Limit; gewinnt
der Timer, bleibt der Instanz-Lock bis zum tatsächlichen Renderende gesetzt.
Einen synchronen Event-Loop-Stall kann er nicht abbrechen;
maxDuration=60ist dafür die äußere Vercel-Grenze. PDFs werden weder persistiert noch als CI-Artefakt veröffentlicht.
Die Tiermatrix übernimmt die bereits implementierten Produktgates, ohne die Produktdaten zu laden:
| Tier | Szenario-Narrative | synthetischer Google-Solar-Block |
|---|---|---|
free | nein | nein |
standard | ja, nur nicht rote Szenarien | nein |
premium | ja, nur nicht rote Szenarien | ja |
planer | nein, tabellarisch | ja |
Der veraPDF-/Poppler-Nachweis ist für alle vier Tiers lokal, in CI und im
geschützten Preview grün. Commit, CI-Jobs, Preview-Deployment, Antwortgrößen,
Zeiten und Request-IDs sind im
Slice-B-Nachweis gebunden.
Der Produktionsreadback belegt für alle vier Tier-POSTs und einen GET leere
404-Antworten auf dem exakten Merge-SHA. Der öffentliche React-PDF-Endpunkt
blieb auf demselben SHA unverändert und weiterhin nicht als PDF/UA getaggt;
Issue #333 bleibt bis echter Produktintegration, Viewerregression,
kontrolliertem PDF/UA-Produktrollout, dessen Live-Readback und
Screenreader-Abnahme offen.
Produktnaher PDF/UA-Report-Shadow (#333, Slice C)
ADR-0061 ersetzt die direkte
renderer-eigene View-Fixture als Eingang durch vier repository-fixierte,
erfundene Objekte des deployten Report-Typs. Ein expliziter Mapper übernimmt
nur Tier, Zeitstand, Fläche, drei Steckbrief-Risiken, sechs Szenarien, drei
Förderprogramme und den optionalen Solarblock. Adresse, Koordinate,
Kataster-, Quellen-, Disclaimer- sowie imagery_quality-/imagery_date-
Rohfelder gelangen nicht in die PDF/UA-View.
Der Mapper prüft Fläche, Zeitstand, Kardinalitäten, Risikostatus und die drei
Solarwerte fail-closed. Narrative kommen aus scenarios[].narrativ und sind
nur in Standard/Premium vorhanden; Google Solar kommt aus solar.google und
nur in Premium/Planer. Die Route, Preview-only-Grenze, Vercel Deployment
Protection, fünf Seiten, zwei Figures, zwei Links und Runtime-Limits bleiben
unverändert; erfolgreiche Antworten tragen nun
X-LandNutzen-PDF-UA-Shadow: slice-c-1.
Der Slice-C-Nachweis ist für
alle vier Tiers lokal und im geschützten Preview mit veraPDF und beiden
Poppler-Backends grün. Er bindet Commit, Deployment, Header, Request-IDs,
negative Pfade und erneut geprüfte PDF-Downloads. Das ist ein produktnaher
Vertragsbeleg, aber kein Zugriff auf gespeicherte oder reale Reports.
Merge-Commit 59036df7aca8a70025efcbf8f0d8dd467700cb9c und
Produktionsdeployment dpl_FbKhtfArLz6wnDTAaRn3aq1bVJh9 belegen für alle
vier Tier-POSTs und einen GET leere private 404-Antworten. Damit ist die
Produktionstrennung verifiziert, nicht der PDF/UA-Produktpfad freigegeben.
Vollständige Realreportsektionen, Karten-Alternativtexte, Viewerregression,
kontrollierter Rollout, Produkt-Live-Readback und Screenreader-Abnahme bleiben
offen; der öffentliche React-PDF-Pfad bleibt unverändert.
Repräsentative PDF/UA-Report-Sektionsparität (#333, Slice D)
ADR-0062 erweitert dieselben vier
synthetischen Report-Fixtures um alle für einen repräsentativen vollständigen
Report vorhandenen Bereiche. Ein fail-closed Mapper erzeugt exakt 31 geordnete
Sektionen aus Lage, Umwelt, Restriktionen und Governance. Fehlende Bereiche,
eine andere Reihenfolge oder unerwartete Kardinalitäten brechen vor dem
Renderer ab.
Der Shadow rendert nun exakt acht A4-Seiten. Drei informative Figures sind
über ID und Alternativtext-Digest gebunden: Parzellenfläche und künstliche ID,
vier gemappte Risikobereiche sowie die Kostenwerte aller sechs Szenarien
bestimmen die jeweiligen Alternativtexte. Damit sind die Texte datenbasiert,
bleiben aber vollständig synthetisch und enthalten weder Adresse noch
Koordinate. Zwei Links und 16 Pagination-Artefakte vervollständigen den festen
Strukturvertrag. Erfolgreiche Preview-Antworten tragen
X-LandNutzen-PDF-UA-Shadow: slice-d-1.
Der Slice-D-Nachweis bindet
die lokale Vier-Tier-Prüfung und den exakten Release: 31 Sektionen, acht
Seiten, drei Figures, veraPDF 1.30.2 sowie beide Poppler-Backends sind lokal,
im geschützten Preview dpl_Ew4nDGJZYUX9fRyiNSuqSz8FFutU und in CI grün.
Merge-Commit 056eafb0eddd0ec6369f71db65862f37b788b152 und
Produktionsdeployment dpl_2bsKrqhys1CuRFQV6QUV6HiSJghg belegen, dass die
interne Route in Produktion für alle vier Tier-POSTs und einen GET mit leeren
privaten 404-Antworten geschlossen bleibt.
Die Route liest weiterhin keine Datenbank, Session, gespeicherten Reports, Providerdaten oder Requestinhalte. Der öffentliche React-PDF-Pfad bleibt unverändert und nicht als PDF/UA getaggt. Echte gespeicherte Reports, Karten-/Viewerregression, kontrollierter Produktrollout, PDF/UA-Live-Readback und manuelle Screenreader-Abnahme bleiben offen; Issue #333 bleibt offen.
Gespeicherter PDF/UA-Report-Roundtrip (#333, Slice E)
ADR-0063 bindet den vollständigen
synthetischen Vier-Tier-Vertrag an den tatsächlichen Store-Pfad. Ein
verpflichtender Integrationstest führt die exakten saveReport()- und
getReport()-SQL-Abfragen gegen das isolierte PostgreSQL-Schema
pdf_ua_stored_report_test aus, schreibt echtes jsonb und rendert erst das
wieder geladene StoredReport-Objekt. Das Schema wird nach dem Lauf entfernt.
Die Persistenznormalisierung ist Teil des Gates: solar.google, die Quelle
solar_google und der zugehörige Disclaimer-Baustein dürfen in keinem Tier
reports.report_json erreichen. Der fail-closed Mapper übernimmt weder
Report-ID, Adresse, Eigentümer-ID noch exakten Opt-in-Zeitpunkt. Die PDF/UA-
View enthält nur den kanarischen Speicherpfad, das Speicherdatum, boolesche
Konto- und Opt-in-Zustände sowie die Aussage, dass Google Solar nicht
persistiert wurde.
Der Preview-Endpunkt liest selbst keine Datenbank, sondern verwendet eine
repository-fixierte Kopie desselben persistenznormalisierten Kanaris.
Erfolgreiche Antworten tragen X-LandNutzen-PDF-UA-Shadow: slice-e-1; der
Vertrag bleibt bei acht Seiten, 31 Sektionen, drei Figures, zwei Links und 16
Pagination-Artefakten. Der öffentliche React-PDF-Pfad bleibt unverändert und
nicht als PDF/UA getaggt. Karten-/Viewerregression, kontrollierter
Produktrollout, PDF/UA-Live-Readback und manuelle Screenreader-Abnahme bleiben
offen; Issue #333 bleibt offen.
PR #541, CI-Lauf 33163812578 und Preview
dpl_AejPoE9E4Joeh9J1ireiHZdKz7k7 belegen den Vier-Tier-Vertrag. Merge-SHA
f5d57d9ac0b4c57a576fa64b9fbddbff49ef8726 und Produktionsdeployment
dpl_GqMcWA5uHvML97ZrciT4dkzPKoQD halten die interne Route für alle vier
Tier-POSTs und einen GET leer 404; dies ist Produktionstrennung, kein
PDF/UA-Produktrollout.
Apple-PDFKit-/Quartz-Viewerregression (#333, Slice F)
ADR-0064 erweitert
npm run qa:pdf:ua-shadow auf macOS um einen zweiten PDF-Stack. Apple PDFKit
lädt die vier im selben Lauf erzeugten synthetischen Slice-E-Dateien und bindet
je acht Seiten, zwei Linkannotationen und acht seitenspezifische Textmarker.
Quartz rendert anschließend 32 Seiten als 1.190 × 1.684 Pixel große
8-Bit-Graustufenbilder; die vorhandenen Rastergrenzen prüfen helle Ränder,
Helligkeit, Schwarzflächen und sichtbaren Body-Ink fail-closed.
Der Slice-F-Nachweis belegt lokal auf macOS 26.6.2 mit Swift 6.3.3 4/4 geladene Dateien und 32/32 gerenderte Seiten. Die mittlere Helligkeit lag zwischen 245,13 und 251,63, der Body-Ink zwischen 2,800 % und 7,428 %. Auf Nicht-macOS-Systemen wird der fehlende Apple-Stack ausdrücklich gemeldet; veraPDF und Poppler bleiben die plattformunabhängigen CI-Gates.
PR #543, CI-Lauf 33166935667 und Preview
dpl_5NCcXGThbbpocRGCsfhS2VsZMg5H belegen den automatisierten Apple-Readback
auf den vier tatsächlich deployten Shadow-Dateien. Merge-SHA
e998d3e42886adef491eb489f5c8a874aca652a3 und Produktionsdeployment
dpl_D4fhMzmmwCDvhGrTcAN3Mm3TMcEL halten die interne Route für vier
Tier-POSTs und einen GET leer 404; dies ist Produktionstrennung, kein
PDF/UA-Produktrollout.
Die nachgelagerte manuelle Abnahme vom 4. September 2026 öffnete die exakt SHA-256-gebundene Premium-Preview-Datei in Apple Vorschau 11.0. Alle acht Seiten waren vollständig sichtbar und mit aktivem VoiceOver per Tastatur erreichbar. Der native Accessibility-Baum enthielt 19 Überschriften, sieben Tabellen, sechs Listen, drei beschriebene Abbildungen und zwei benannte Links; VoiceOver wurde danach wieder ausgeschaltet. Damit sind Apple Vorschau und die VoiceOver-Stichprobe für den synthetischen Premium-Shadow abgeschlossen.
Route und Erfolgsheader bleiben slice-e-1, der öffentliche React-PDF-Pfad
bleibt unverändert. Chrome/PDFium-Tag-Semantik, echte Karteninhalte, kontrollierter
Produktrollout und PDF/UA-Live-Readback eines echten Nutzerreports bleiben
offen; Issue #333 bleibt offen.
Zugänge (Parität)
- UI:
/report→ nach Erzeugung „Permalink" + „PDF herunterladen". - API: siehe Tabelle, OpenAPI unter /docs/api.
- MCP: Tool
export_report→{ permalink, pdf_url }. - Skill:
report.mjsgibt am Ende Permalink + PDF-URL aus.
Grenzen
„Wer den Link hat, sieht den Report" (Teilen-Use-Case) — Schutz =
unerratbare ID + noindex + 90-Tage-Retention. Privatere Modi
(Auth/Ablauf-Token) sind als Folge möglich.
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.