ADR-0063: Gespeicherter Report als PDF/UA-Shadow-Eingang

  • Status: angenommen; implementiert, CI-/Preview-verifiziert und mit belegter Produktionstrennung ausgeliefert
  • Datum: 28. August 2026
  • Issue: #333 bleibt offen
  • Vorgänger: ADR-0062

Kontext

Slice D belegt 31 repräsentative Reportsektionen, drei datenbasierte Alternativtexte und eine vollständige achtseitige PDF/UA-Struktur. Der Eingang ist jedoch ein direkt im Repository erzeugtes Report-Objekt. Der produktive PDF-Endpunkt lädt dagegen zuerst einen Snapshot aus reports.report_json über getReport(). Dabei werden Google-Solar-Daten vor dem Speichern entfernt und der separat gespeicherte Disclaimer-Opt-in-Zeitstempel beim Lesen wieder in den Report eingebunden.

Ein direkter Preview- oder Produktionszugriff auf gespeicherte Nutzerreports wäre für diesen Zwischenschritt unnötig und würde die bislang enge Datenschutzgrenze des Shadows aufgeben.

Entscheidung

  1. Slice E bindet den Shadow an einen synthetischen gespeicherten Report. Im verpflichtenden PostgreSQL-Gate wird derselbe vollständige Vier-Tier-Report über die exakten saveReport()- und getReport()-SQL-Abfragen in ein isoliertes Testschema geschrieben und wieder gelesen. Erst das geladene StoredReport-Objekt wird an den PDF/UA-Renderer übergeben.
  2. Das Gate prüft für free, standard, premium und planer die reale jsonb-Serialisierung, Ablauf- und Eigentümerfelder sowie die Store-Normalisierung. Insbesondere dürfen solar.google, die Quelle solar_google und der zugehörige Disclaimer-Baustein den persistenten Snapshot niemals erreichen.
  3. Der Mapper schließt fail-closed. Er akzeptiert nur eine gültige nicht-enumerierbare Report-ID, eine vollständig synthetische Adresse, keine Koordinate, einen parsebaren Speicherzeitpunkt und einen bereits von Google-Solar-Rohdaten bereinigten Report. Report-ID, Adresse, Eigentümer-ID und exakter Opt-in-Zeitpunkt werden nicht in die Renderer-View übernommen.
  4. Die Renderer-View PDF-UA-SLICE-E-1 enthält ausschließlich minimierte Persistenzmerkmale: Vertragsversion, kanarischer reports.report_json-Pfad, Speicherdatum, boolesche Konto- und Opt-in-Zustände sowie die feste Aussage, dass Google Solar nicht persistiert ist. Die vorhandenen 31 Sektionen, acht Seiten, drei Figures, zwei Links und 16 Pagination-Artefakte bleiben unverändert.
  5. Der Preview-Endpunkt bleibt POST /api/internal/pdf-ua-shadow/[tier], ohne Body oder Query, hinter Vercel Deployment Protection und ausschließlich in VERCEL_ENV=preview. Er liest keine Datenbank, sondern rendert eine repository-fixierte Kopie des nach dem Store normalisierten Kanaris. Erfolgreiche Antworten tragen X-LandNutzen-PDF-UA-Shadow: slice-e-1.
  6. Produktionsdatenbanken, gespeicherte Nutzerreports, Provider, Sessions, Adressen und Koordinaten werden weder lokal noch im Preview gelesen. Das PostgreSQL-Gate verwendet ausschließlich das kurzlebige CI-Testschema pdf_ua_stored_report_test und entfernt es am Ende.
  7. Der öffentliche Endpunkt GET /api/v1/report/{id}/pdf bleibt bei React-PDF und weiterhin nicht als PDF/UA getaggt. Slice E ist kein Produktrollout.
  8. Issue #333 bleibt offen. Es fehlen weiterhin die Karten-/Viewerregression des neuen Renderers, ein kontrollierter Produktrollout mit echtem Produkt-Live-Readback und die manuelle Screenreader-Abnahme.

Konsequenzen

  • Ein grüner Slice-E-Lauf belegt nicht nur Typ- und Sektionsparität, sondern den tatsächlichen PostgreSQL-jsonb-Roundtrip des gespeicherten Reportvertrags.
  • Die Google-Solar-Tierunterschiede enden bewusst an der Persistenzgrenze; Standard und Premium behalten ihre erlaubten Szenario-Narrative, aber kein gespeichertes Tier enthält Google-Solar-Rohdaten.
  • Der Shadow bleibt reproduzierbar und frei von Produktionsdaten. Der spätere Produktrollout benötigt deshalb weiterhin einen eigenen, kontrollierten Daten- und Viewer-Nachweis.

Referenzen