presseportale/dev/2026-09-11-audit/bildkennzeichnung-entwicklungsplan-2026-09-21.md
Kevin Adametz 448643b4d8 dev: Commit-Hashes der Blöcke 9 und 10 im Protokoll nachgetragen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:09:13 +00:00

52 KiB
Raw Blame History

Entwicklungsplan: Bildnachweis, KI-Kennzeichnung, Rechtebestätigung, Beweisarchiv

Stand: 22.09.2026, Fassung 2 (Entscheidungen getroffen; Blöcke A bis H und K umgesetzt und committet, Deploy offen; Blöcke I und J stehen aus) Grundlage: Umsetzungsvorschlag 15.09., Feedback-Audit 15.09., Decision-Update 22.09. und die Antworten des Betreibers vom 21.09. auf die Entscheidungsfragen der Fassung 1. Bei Widerspruch gilt das Decision-Update (im Folgenden „DU"). Code-Abgleich an HEAD e620ef3 auf technik/betriebsfixes-2026-09. Zweck: Alle Punkte aus den vier Quellen, die am heutigen Code umsetzbar sind, in einer abarbeitbaren Reihenfolge mit Fix, Test und Aufwand. Gilt zuerst für das Titelbild einer Pressemitteilung; Newsroom-Bilder (Roadmap 1b.3) nutzen denselben Unterbau.

Die Fassung 1 vom 21.09. enthielt eine Fragenliste und vier Textanlagen für den Anwalt. Beides ersetzt das Decision-Update (Abschnitte 3 und 7) und ist hier gestrichen.


1. Fazit

Die Richtung des Vorschlags trägt, die Korrekturen des Feedback-Audits sind übernommen, und das Decision-Update hat die offenen Geschäfts- und Rechtsfragen mit Defaults belegt. Der Umfang ist damit klar und größer als in Fassung 1:

  • Formular und Beweisstand für Bilder wie geplant: drei Blöcke, zwei Pflichtangaben plus Bestätigung, Original privat, Metadaten gelesen, Zustimmung mit rekonstruierbarem Wortlaut. Sperre nur bei geprüfter Signatur (Phase 2).
  • Beweisarchiv als eigener Baustein, der über Bilder hinausgeht: verschlüsselter, zugriffsprotokollierter Datensatz zu jeder entfernten Veröffentlichung (Bild, Depublikation, DSGVO-Änderung, Kennzeichnungskorrektur), drei Jahre zum Jahresende, legal_hold, Löschung nach Restore. Nie veröffentlichte Entwürfe werden sofort gelöscht.
  • Vertragliche Einbeziehung: Bestätigungstext an die AGB-Fassung gebunden; zusätzlich ein Zustimmungsgate je Dokumentversion vor jeder Einreichung.
  • Öffentliche Sprache: „automatisierte Prüfung" statt „geprüft" und „qualitätsgeprüft"; kein Pro-Prüfversprechen.
  • Reihenfolge: Deploy 1b (Formular, Archiv, Protokoll) geht vor der Anwaltsantwort live. AGB v0.7 und DSE v0.4 folgen danach.

Was der Code-Abgleich ergeben hat

Befund Konsequenz
Dev-Kopie: 14.601 Bilder. 14.554 Legacy-Importe, davon 3.786 an veröffentlichten PMs und 10.768 an inaktiven (10.463 rejected = im Altsystem „offline genommen", 301 archiviert, 4 Entwürfe). 47 Bilder aus dem neuen System (32 an Entwürfen, 15 veröffentlicht), 45 davon mit Rechtebestätigung, 17 als KI gekennzeichnet E-B11: die 10.768 werden gelöscht (E.5). Der Bestand betrifft danach nur noch 3.786 Bilder an veröffentlichten PMs.
Weder exiftool noch c2patool im Container. Imagick 6.9 liest IPTC-, XMP- und EXIF-Profile und kann WebP; PHP-Erweiterung exif vorhanden Phase 1 ohne neue Binärdatei. c2patool erst Phase 2, dann in Sail-Image und auf dem Ploi-Server.
ImageService::storePressReleaseImage() löscht das Original nach der Variantenerzeugung und fällt bei fehlender Cover-Variante auf die Quelldatei zurück Uploader sichert das Original vorher; der Rückfall wird zum Fehler. GD schreibt keine Metadaten in Varianten, die Bereinigung ist heute schon gegeben (Test friert das ein).
Der heutige Bestätigungstext („Rechte bestätigt" plus Beschreibungssatz im Bildmanager, Zeile 710) hat keine Kennung. 45 Bestätigungen tragen nur rights_confirmed_at DU S3: dieser Wortlaut wird rückwirkend als img-confirm-v1 archiviert, die 45 Zeilen bekommen einen Verweis darauf. Neue Kennungen ab img-confirm-v2.
Bildmanager kann bestehende Bilder nur löschen, nicht bearbeiten. assertAiDeclarationComplete() blockt das Einreichen jeder PM mit Bild ohne ai_declaration Nach E.5 gibt es keine Legacy-Bilder an wiedereinreichbaren PMs mehr; kein Bestandsschutz nötig.
Legacy-API: POST /service/pressreleases/{id}/images setzt original fest, keine Statusprüfung. connektar liefert keine Bilder E-B4: Endpunkt wird abgeschaltet (C.2).
Öffentliche Wortwahl: „qualitätsgeprüft" in lang/de/web.php (Zeilen 189, 231, 233, 249, 255), Footer-Tagline, presseecho.blade.php:43; „Geprüfte Mitteilungen" in lang/de/static.php:533; „geprüft" in richtlinien.blade.php:36; „redaktionelle Qualität" in agb.blade.php:139, 206. Die „priorisierte redaktionelle Prüfung" ist im PlanSeeder bereits gestrichen DU S10 (D.6). Die AGB-Stellen gehen mit v0.7 an den Anwalt.
Depublikation, Archivierung, Statuswechsel und Löschung laufen über PressReleaseService (depublish, archive, changeStatusFromAdmin, deleteFromAdmin); DSGVO-Änderungen über AnonymizationService und CorrectionService Das sind die Anbindungspunkte des Beweisarchivs (F.4).
OpenGraph-Bild ist heute die Detail-Variante (release-detail.blade.php:168); Teaser nutzen card/thumb ohne Nachweis; RSS-Feeds ohne Bilder; JSON-LD image als URL-Liste DU E6: Feeds bleiben ohne Bilder. DU E7: OG-Variante mit Nachweisbalken und Teaser mit Nachweis (H).
Kundenoberfläche durchgehend „Sie"; AGB sagen „Anbieter" Alle neuen Texte in Sie-Form mit „Anbieter".
Im Decision-Update referenziert, aber nicht im Repository: Umsetzung_Bildnachweis_KI-Kennzeichnung.md, handover_loesch-anfragen.md DU S19 (Löschanfragen-Handover) kann erst bearbeitet werden, wenn das Dokument im Ordner liegt.

2. Entscheidungen

Alle Fragen der Fassung 1 sind beantwortet (Betreiber, 21.09.2026). Wo das Decision-Update vom 22.09. abweicht, gilt es.

Nr. Frage Entscheidung Quelle
E-B1 Phase 1 ohne automatische Sperre, nur Vorbelegung mit protokollierter Abweichung; Sperre in Phase 2 nach C2PA-Test ja Betreiber; DU E9
E-B2 Aufbewahrungsfrist drei Jahre nach dem 31.12. des Entfernungsjahres, legal_hold bei Streit und Regress; nie veröffentlichte Uploads sofort löschen. Anwalt bestätigt (DU A4) Betreiber; DU E3, E4
E-B3 Bestand ohne Bildnachweis revidiert durch DU 3.4: ohne Bestätigungsprotokoll keine Verantwortlich-Zeile. Bestand zeigt „Foto: [Nachweis]", ohne Nachweis „Quelle: [Firma]" DU 3.4
E-B4 Legacy-API ohne die neuen Pflichtfelder Legacy-Bildupload abschalten. Wird nicht genutzt; wer Bilder liefern will, nimmt v1 Betreiber
E-B5 algorithmicallyEnhanced kein KI-Hinweis, nur interner Vermerk Betreiber
E-B6 Upload-Grenze Formular und API 16 MiB, Pixelgrenze 8.000 × 8.000, vor der Prüfung Betreiber
E-B7 Widerspruch gegen Vorbelegung oder Sperre Kontaktweg für den Kunden, protokollierter Admin-Korrekturweg mit Zwischenzustand „vorläufig zurückgehalten" Betreiber; DU S17
E-B8 Anrede Sie Betreiber
E-B9 Admin-Upload revidiert durch DU E10: Veröffentlichung erst nach Kundenbestätigung per Magic-Link „Bild bestätigen", gleiches Protokoll DU E10
E-B10 Hartes Löschen Original und Bestätigung überleben das Löschen bis zum Fristende (im Beweisarchiv) Betreiber; DU 4
E-B11 Wiedereinreichen alter PMs mit Legacy-Bild kein Bestandsschutz. Legacy-Bilder an nicht veröffentlichten PMs (rejected, archived, draft) werden gelöscht; eine erneute Einreichung läuft ohne Bild Betreiber
E-B12 Bestätigungstabelle und Uploader polymorph für Newsroom-Bilder ja Betreiber
E-B13 Overlay-Icon eigenes „i" im Kreis, kein „cr"-Logo Betreiber

Aus dem Decision-Update zusätzlich übernommen (dort E1 bis E14): Kontoinhaber ist Vertragspartner, Firmenprofil nur Zuordnung (E1); Original immer speichern plus veröffentlichte Fassung (E2); „veröffentlicht" = hatte irgendwann eine öffentliche URL, auch kurz (E5); RSS ohne Bilder (E6); OG-Variante mit Nachweisbalken, Teaser mit Nachweis (E7); kein Erstattungsausschluss bei Bildentfernung (E8); „automatisierte Prüfung", kein Pro-Prüfversprechen (E11); Zustimmungsgate je Dokumentversion (E12); UrhDaG nur interner Vermerk (E13); Deploy 1b vor der Anwaltsantwort (E14).

Offene Punkte (entschieden 22.09.2026)

Nr. Punkt Entscheidung
O1 DU 4.1: „Einreichung im Freigabelauf abgelehnt, nie veröffentlicht: Mediendateien sofort löschen." Eine abgelehnte PM kann der Kunde korrigieren und erneut einreichen; das Bild wäre dann weg. Entschieden: Medien erst löschen, wenn die abgelehnte PM durch den bestehenden Entwurfs-Purge fällt oder gelöscht wird, nicht im Moment der Ablehnung.
O2 Zustimmungsgate für API-Nutzer (DU S12): je Request, je Token oder je Kontoinhaber? Entschieden: einmal je Kontoinhaber und Dokumentversion über POST /api/v1/legal/acceptances; ohne gültige Annahme antwortet submit mit 428 und den geforderten Versionen.
O3 Die 301 Legacy-Bilder an archivierten PMs: darunter können depublizierte Meldungen sein, für die das Beweisarchiv künftig drei Jahre gälte. Entschieden: mit E-B11 löschen; der Altbestand hat kein Bestätigungsprotokoll, das ein Archiv tragen würde.
O4 Schrift für den eingebrannten Nachweisbalken (H.1) muss als TTF im Repository liegen. Entschieden: Inter liegt bereits unter resources/fonts/ (TTF, 18pt-Schnitte) mit OFL.txt (SIL Open Font License 1.1). H.1 nutzt Inter_18pt-SemiBold.ttf, kein neuer Font. Achtung: der Ordner ist heute untracked (nicht ignoriert, nur nie eingecheckt); mit H.1 werden Inter_18pt-SemiBold.ttf und OFL.txt committet, sonst fehlt die Schrift auf Live.
O5 Pro-Tarif nach Streichung des Prüfversprechens (DU 11.5) Offen, wird geklärt, wenn D.6 die Preisseite anfasst. Betreiber.

3. Abgleich mit dem Decision-Update (Backlog S1 bis S24)

DU Inhalt Im Plan Bemerkung
S1 Originalarchiv und Snapshot der veröffentlichten Fassung A.6, E.2 Snapshot beim Veröffentlichen, nicht erst beim Entfernen
S2 Bestätigungsprotokoll A.2 polymorph (E-B12)
S3 Tabelle unveränderlicher Bestätigungstexte, heutige Kennung A.3 heutiger Wortlaut = img-confirm-v1
S4 API v1 Pflichtfelder, 422 C.1
S5 Legacy-Default entfernen, Altbestand legacy C.2, A.1 Endpunkt wird abgeschaltet (E-B4); legacy = rules_version IS NULL
S6 Kundenangabe und Portalkennzeichnung getrennt A.1 ai_declaration bleibt die Portalkennzeichnung (alle Leser), dazu ai_declaration_user und ai_declaration_source
S7 Admin-Upload mit Kundenbestätigung per Magic-Link B.4
S8 published_ever an PM und Bild E.1
S9 Sofortlöschung nie veröffentlichter Uploads E.3 mit O1
S10 Wortwahl „automatisierte Prüfung" D.6 Fundstellen in Abschnitt 1
S11 Bestätigungstext mit aufgelöster AGB-Fassung rendern A.3, B.1
S12 legal_document_acceptances, Zustimmungsgate G mit O2
S13 archive_entries, archive_access_log, Löschjob, legal_hold, Post-Restore, Tombstone-Export F.1 bis F.3
S14 Archivierung bei Depublikation, DSGVO-Änderung, Korrektur F.4
S15 OG-Variante, Teaser mit Nachweis H.1, H.2
S16 Beschnitt-Steuerung H.3
S17 Korrekturweg mit Zwischenzustand und Archiveintrag E.6
S18 Zugriffsprotokoll Originalarchiv E.4
S19 Löschanfragen-Handover ergänzen – Dokument nicht im Repository
S20 C2PA-Verifikation I.1 bis I.4
S21 Metadaten in Varianten schreiben I.5 Imagick statt exiftool, gleiche Wirkung
S22 Re-Sync-Sperre für Bilder I.6
S23 Bildklausel im Syndikationsvertrag – Betreiber
S24 KI-Vorab-Check für Bilder (DU Abschnitt 12, E15) K.1 bis K.6 Sichtung auf offensichtliche Probleme, keine Rechteprüfung; Stufe 1 nur Stock-Wasserzeichen, Stufe 2 alle Kategorien; direkt nach Deploy 1b

Nicht aufgenommen

  • Externe Bild-URLs und Sammelupload images[] in der API (Feedback F04).
  • Umbenennung copyright in credit; vierter Enum-Wert not_declared (NULL leistet dasselbe, alle Leser prüfen darauf).
  • Inhaltliche Rechteprüfung von Bildern; Nachprüfung des veröffentlichten Bestands. (Die eng zugeschnittene Sichtung auf offensichtliche Probleme aus DU Abschnitt 12 ist seit dem Update vom 22.09.2026 als Block K aufgenommen; sie prüft keine Rechte und wird öffentlich nicht als Bildprüfung beschrieben.)
  • Bilder in RSS-Feeds (DU E6).
  • Reduziertes Original bei Bildern ohne C2PA (DU 8: nur als Reaktion, falls der Anwalt die Datenminimierung anders sieht).

4. Aufwandsskala und Arbeitsregeln

Kürzel Bedeutung
S eine Stunde bis ein halber Tag
M ein bis drei Tage
L länger oder abhängig von einer Entscheidung

Wie im Audit-Entwicklungsplan: erst der Test, der am heutigen Code rot ist, dann der Fix, dann die betroffene Testdatei, am Blockende die gesamte Suite, PHPStan und Pint. Vor Testläufen php artisan config:clear (Config-Cache-Falle).

Der Flag IMAGE_RIGHTS_V2 bleibt als Deploy-Sicherung: Deploy 1 geht mit false live, wird auf Live geprüft und dann eingeschaltet. Er wartet nicht auf den Anwalt (DU E14).


Block A — Datenmodell, Texte, Prüfung, Uploader

Nr. Arbeitspaket Aufwand Abhängigkeit
A.1 Migration press_release_images, alles nullable: ai_declaration_user (Angabe des Kunden), ai_declaration_source (Enum: customer, iptc_prefill, c2pa_verified, admin_correction), ai_declaration_locked (bool, default false), credit_source (customer, metadata, api), provenance_status (Enum A.4), provenance (json), provenance_checked_at, provenance_tool_version, original_disk, original_path, original_sha256 (64), published_snapshot_path, published_snapshot_sha256, published_ever_at, customer_confirmation_requested_at (B.4), rules_version (string; NULL = Bestand, 2026-10 = Formular v2), focus_x/focus_y (tinyint, H.3), no_crop (bool, H.3). ai_declaration bleibt die Portalkennzeichnung, weil alle Leser (Artikelseite, Kunden- und Adminansicht, PDF, Snapshot in press_release_ai_declarations) daran hängen. license_url auf 2048 Zeichen. Bestand unangetastet. Test: vorhandene Werte unverändert. S keine
A.2 Tabelle image_rights_confirmations, insert-only (Observer wirft bei Update oder Delete, Ausnahme E.3): subject_type/subject_id (polymorph, Phase 1 PressReleaseImage), press_release_id, company_id (Zuordnung), user_id (Kontoinhaber = Vertragspartner, DU E1), personal_access_token_id, channel (form, api, magic_link), legal_text_id (A.3), text_snapshot (gerenderter Wortlaut mit aufgelöster AGB-Fassung und Link), agb_version, locale, file_sha256, declaration_snapshot (json: copyright, license_url, source_url, ai_declaration_user, ai_declaration, ai_declaration_source, locked, provenance_status, provenance), ip_hmac (HMAC-SHA256 mit eigenem Schlüssel CONFIRMATION_IP_KEY und Versionskennung), user_agent (255), confirmed_at. Fremdschlüssel auf Bild und PM mit nullOnDelete, Snapshot-Spalten tragen die Werte weiter (E-B10). Test: hartes Löschen des Bildes lässt die Bestätigung stehen. S A.1
A.3 Textarchiv legal_texts (insert-only): key (z. B. img-confirm-v2), type (image_rights_confirmation), locale, title, body (mit Platzhaltern {agb_version}, {agb_date}, {agb_url}, {portals}, {sections}, {indemnity_sections}), bound_agb_version, body_sha256, effective_from, note. Seeder: img-confirm-v1 = heutiger Wortlaut aus dem Bildmanager (Zeile 710), gebunden an AGB 0.6; img-confirm-v2 = Langtext und Kurzform aus DU 3.3 mit Verweis „§ 8 und § 9" / „§ 8 Abs. 2", gebunden an AGB 0.6; img-confirm-v3 = dieselben Texte mit „§ 8, § 8a und § 9" / „§ 8 Abs. 2 und § 8a Abs. 5", gebunden an AGB 0.7, effective_from leer bis Deploy 3. Kennungen werden nie wiederverwendet. LegalText::currentFor($type, $agbVersion, $locale). Backfill: die 45 bestehenden Bestätigungen bekommen je eine Zeile in A.2 mit legal_text_id = v1, agb_version = 0.6, confirmed_at = rights_confirmed_at, channel = form, ohne IP und UA. Test: Wortlaut byte-genau rekonstruierbar, auch nachdem v3 aktiv ist. S A.2
A.4 Enums: ImageProvenanceStatus (absent, unsigned_metadata, validated_trusted, validated_untrusted, invalid, unsupported, error), AiDeclarationSource (wie A.1), CreditSource. AiImageDeclaration::rank() (0, 1, 2) für die Portalregel „Kennzeichnung = max(geprüfter Befund, Kundenangabe)", mit Kommentar, dass das eine Portalregel ist (Feedback F11). label() von Original wird „Ohne generative KI erstellt oder bearbeitet"; publicLabel() unverändert. S keine
A.5 App\Services\Image\ImageProvenanceInspector: Imagick (getImageProfile('iptc'), getImageProfile('xmp')) und exif_read_data(). Felder: IPTC 2:110 Credit, 2:80 By-line, 2:116 Copyright; XMP photoshop:Credit, dc:creator, dc:rights, Iptc4xmpExt:DigitalSourceType; EXIF Artist, Copyright. IPTC-URIs normalisiert (Lang- und Kurzform). Zuordnung: trainedAlgorithmicMedia → Vorschlag ai_generated; compositeWithTrainedAlgorithmicMedia → ai_edited; algorithmicallyEnhanced → nur enhancement_hint, kein Vorschlag (E-B5); digitalCapture, negativeFilm, positiveFilm, print → Vorschlag original; sonst keiner. Nachweis-Vorschlag: Credit → dc:creator → By-line → EXIF Artist → CopyrightNotice, auf 255 gekürzt mit Vermerk. Status in Phase 1 nur absent, unsigned_metadata oder error. Dauer gemessen und im json abgelegt; Ausnahme oder über 10 s → error mit Fehlerklasse. Nicht ausgewertet: CreatorTool, Dateiname, Bildinhalt. Fixtures unter tests/Fixtures/images/ (mit Imagick erzeugt): Kamera-JPEG mit IPTC, JPEG mit XMP trainedAlgorithmicMedia, JPEG mit algorithmicallyEnhanced, PNG ohne Metadaten, WebP mit XMP, beschädigte Datei. M A.4
A.6 App\Services\PressRelease\PressReleaseImageUploader::store(UploadedFile, HasImages $subject, ImageSubmission, Actor): (1) Größe und Pixel prüfen (E-B6), Bytes einmal lesen, SHA-256; (2) Original auf Disk local nach press-release-originals/{pm}/{uuid}.{ext}, Schreibvorgang prüfen; (3) Prüfung auf dieselben Bytes; (4) Derivate über ImageService (A.7), bei Fehler alles zurückrollen; (5) ai_declaration_user = Angabe, ai_declaration = max(Vorschlag aus geprüftem Befund, Angabe) (Phase 1: Vorschlag aus unsigned_metadata fließt nicht ein, nur Vorbelegung; Abweichung nach unten wird im json als deviated_from_prefill vermerkt), ai_declaration_source = customer; (6) Transaktion mit lockForUpdate() auf der PM: ein-Titelbild-Regel, Bildzeile, Bestätigung (außer Kanal Admin, B.4), rules_version; (7) DB-Fehler → Dateien löschen. ImageSubmission: copyright, licenseUrl, sourceUrl, title, description, aiDeclaration, rightsConfirmed, legalTextKey, focus, noCrop. Actor: User, Token, Kanal. Test: DB-Fehler hinterlässt keine Datei; zwei gleichzeitige Uploads ergeben ein Titelbild; Hash von Original, Bestätigung und Prüfung identisch. M A.1 bis A.5, A.7
A.7 ImageService::storePressReleaseImage(): kein Rückfall auf die Quelldatei, RuntimeException stattdessen; neue Variante og (1200 × 630, H.1); resizeCover mit Fokuspunkt und no_crop (H.3). Quelldatei im Derivat-Ordner wird nach der Erzeugung gelöscht (Original liegt separat). Test: fehlende Cover-Variante landet nie öffentlich; Varianten ohne EXIF-GPS und Seriennummer. Erwartung „Originaldatei gelöscht" in PressReleaseImageLicenseTest umschreiben. S keine

Block B — Formular v2 und Admin-Upload

Nr. Arbeitspaket Aufwand Abhängigkeit
B.1 press-release-images-manager.blade.php umbauen (Flag an): Block 1 Bild mit Vorschau, Prüfhinweis, Fokuspunkt-Wahl (H.3); Block 2 Bildnachweis (Pflicht, vorbelegt aus Metadaten, sonst Firmenname, sichtbar als „Vorschlag, bitte prüfen"), KI-Kennzeichnung (Pflicht, drei Optionen mit neuem Label, Erklärtext), aufklappbar „Optionale Angaben" (Titel, Bildunterschrift, Lizenz-URL, Quellen-URL getrennt); Block 3 Bestätigung als flux:switch mit Kurzform aus DU 3.3 (inkl. AGB-Datum), „Details" klappt den gerenderten Langtext aus A.3 auf, Kontaktweg für Widerspruch daneben. Vier Zustände: A validated_trusted (Phase 2), B absent, C unsigned_metadata („Vorbelegt aus den Metadaten der Datei, Sie können die Auswahl ändern"), D error („Herkunftsdaten konnten nicht geprüft werden", Speichern erlaubt, Vermerk). Entfallen: author als Pflicht, license_type, license_detail, Personen-, Marken-, Notizfelder, „Anbieter-Bedingungen geprüft". M A.6
B.2 Bindung an die Datei (Feedback F09): Prüfung nach updatedNewImage auf der Temp-Datei, Ergebnis und Hash in #[Locked]-Properties; beim Speichern rechnet der Uploader neu, Abweichung → Abbruch. Dateiwechsel setzt Vorbelegung und Schalter zurück. Manuell geänderter Nachweis wird nicht überschrieben. Effektive Werte serverseitig. S B.1
B.3 Tests: PressReleaseImageRightsV2Test ersetzt die Lizenz-Tests (alte bleiben für Flag aus, bis der Flag fällt); PressReleaseAiDeclarationTest um Snapshot-Erwartungen (Kundenangabe und Portalkennzeichnung getrennt). M B.1, B.2
B.4 Admin-Upload (DU E10, S7): Admin lädt hoch ohne Bestätigung; Bild bekommt customer_confirmation_requested_at, Status in der Karte „wartet auf Kundenbestätigung"; Mail „Bild bestätigen" mit Magic-Link an den Kontoinhaber (bestehende Magic-Link-Infrastruktur, Herkunft image_confirmation, erneut anforderbar); Landeseite zeigt Bild, Angaben und den Schalter mit Langtext; Bestätigung mit channel = magic_link in A.2. Veröffentlichungsgate (E.1) lässt ein Bild ohne Bestätigung nicht öffentlich werden; der Admin sieht das beim Freigeben. Admin-Bildtausch an einer veröffentlichten PM läuft genauso (neue Bestätigung zur neuen Datei). Test: ohne Klick keine Bestätigung, PM nicht veröffentlichbar mit Bild; nach Klick Protokoll mit Kontoinhaber. M A.2, A.6

Block C — API

Nr. Arbeitspaket Aufwand Abhängigkeit
C.1 StorePressReleaseImageRequest (Flag an): copyright required, ai_declaration required, rights_confirmed accepted, confirm_text_version required und aktive Kennung (sonst 422 mit current_version), license_url, source_url, focus_x, focus_y, no_crop optional; Grenze 16 MiB (E-B6). Controller ruft den Uploader mit channel = api und Token-ID. PressReleaseImageResource kompatibel ergänzt: ai_declaration (Portal), ai_declaration_user, ai_declaration_source, provenance_status, rights_confirmation {version, confirmed_at}, warnings[] (provenance_check_failed, Phase 2 ai_declaration_raised_by_metadata). urls.original bleibt das Cover, dokumentiert. Tests in PressReleaseImageApiTest. M A.6
C.2 Legacy-Bildupload abschalten (E-B4): POST /service/pressreleases/{id}/images antwortet im Legacy-Fehlerformat mit 410 und Hinweis auf v1; GET …/images bleibt. Controller und Legacy-Tests entfernen bzw. auf 410 umstellen; dev/betrieb/api-legacy-paritaet.md ergänzen. StorePressReleaseImageRequest verliert die Legacy-Ausnahme. S keine
C.3 API-Doku (dev/api/, öffentliche API-Seite): Pflichtfelder, 422, Bestätigungstext mit Kennung, AGB-Fassung über die API-Nutzungsbedingungen (DU 3.3), Abschaltung des Legacy-Uploads. S C.1, C.2

Block D — Ausgabe und öffentliche Sprache

Nr. Arbeitspaket Aufwand Abhängigkeit
D.1 App\Support\ImageCredit::for(PressReleaseImage, ?Company): eine Auflösung für alle Ausgaben. Nachweiszeile: „Foto: [copyright]", sonst „Foto: [author]", sonst (Bestand) „Quelle: [Firma]" (DU 3.4). KI-Zusatz aus publicLabel(), Bestand-Fallback is_ai_generated. Verantwortlich-Zeile „Bild und Angaben: [Firma], eingereicht am [Datum]" nur mit Bestätigungsprotokoll. Lizenz- und Quellenlink nur http/https. Ersetzt die vier Einzelimplementierungen (Artikelseite, Kunden- und Adminansicht, ProofPdfRenderer::imageCreditLines()). S A.2
D.2 Overlay auf der Artikelseite (beide Portale, anonyme Vorschau): rundes Icon unten links, 28 px, eigenes „i" (E-B13). Alpine.js, aria-haspopup, aria-expanded, Fokus hinein und zurück, Escape, unter sm Bottom-Sheet. Inhalt: Nachweis, Lizenz/Quelle, KI-Hinweis mit Quelle der Kennzeichnung („Angabe des Einreichers" / „aus geprüften Herkunftsdaten" / „aus Metadaten vorbelegt", DU 3.4), Verantwortlich-Zeile (nur mit Protokoll), „Bild melden". Keine Content-Credentials-Zeile in Phase 1. Manuelle Abnahme mit Tastatur und Screenreader. M D.1
D.3 Nachweiszeile unter dem Bild bleibt ohne Klick sichtbar. Kunden- und Adminansicht zeigen zusätzlich Bestätigungskennung, AGB-Fassung, Zeitpunkt und Kanal; bei Admin-Uploads den Zustand „wartet auf Kundenbestätigung". S D.1
D.4 ProofPdfRenderer: Zeilen aus D.1 plus Bestätigungskennung und Zeitpunkt; alte Felder bleiben für Bestandsbelege lesbar. S D.1
D.5 Web-Cache: WebCaches::forgetPublicPages() (bzw. die gezielte Variante für die Detailseite) bei Bild-Upload, -Löschung, Kundenbestätigung und Korrektur. S A.6
D.6 Wortwahl (DU E11, S10): richtlinien.blade.php:36 durch den Absatz aus DU 3.5 ersetzen und den Abschnitt „Bilder" aus DU 3.5 einfügen, Version richtlinien in legal_documents hochzählen; „qualitätsgeprüft" und „geprüfte" in lang/de/web.php (189, 231, 233, 249, 255), static.php:533, presseecho.blade.php:43 und der Footer-Tagline auf „automatisierte Prüfung" bzw. neutrale Formulierung; Ablehnungs- und Feedbackmails auf „Unsere automatisierte Prüfung hat …" (Fundstellen in den Mail-Templates und lang/de beim Umbau suchen). Die beiden AGB-Stellen (139, 206) bleiben bis v0.7. Test: LegalDocumentTest für die neue Richtlinien-Version; Snapshot-Test der geänderten Lang-Keys. S keine

Block E — Lebenszyklus, Original, Korrektur, Bereinigung

Nr. Arbeitspaket Aufwand Abhängigkeit
E.1 published_ever_at an PM und Bild (DU S8, E5): gesetzt in publish(), changeStatusFromAdmin() nach Published, restoreFromGone() und für Bilder beim Upload an eine bereits veröffentlichte PM. Backfill: PMs mit published_at oder Status-Log nach published; deren Bilder ebenso. Veröffentlichungsgate: publish() verlangt für jedes Bild mit rules_version eine Bestätigung zur aktuellen original_sha256 (A.2); fehlt sie (Admin-Upload ohne Kundenbestätigung), Abbruch mit klarer Meldung. Test je Pfad. S A.1, A.2
E.2 Snapshot der veröffentlichten Fassung (DU E2, S1): beim ersten Veröffentlichen der PM wird je Bild die Cover-Variante plus ein JSON mit gerendertem Nachweis, Portalkennzeichnung und Quelle nach `press-release-published/{pm}/{image}-{sha}.{ext json}auf Disklocalkopiert,published_snapshot_*` gesetzt. Bei Bildtausch an veröffentlichter PM neuer Snapshot. Test: Snapshot entspricht der ausgelieferten Variante. S
E.3 Sofortlöschung nie veröffentlichter Uploads (DU E4, S9, mit O1): remove(), v1 destroy(), Ersatz durch Upload, PurgeExpiredPressReleaseDrafts und Löschung der PM löschen bei published_ever_at IS NULL Original, Derivate, Snapshot und Bestätigung (harte Löschung der Bestätigungszeile ist hier gewollt; der Observer aus A.2 lässt genau diesen Fall zu). Bei gesetztem published_ever_at: Übergabe an das Beweisarchiv (F.4), danach Derivate löschen, Soft Delete. Test je Fall. M E.1, F.4
E.4 Zugriff auf Original und Snapshot nur über GET /admin/press-releases/{pm}/images/{image}/original und …/published mit Admin-Recht, jeder Abruf in archive_access_log (F.1) mit Pflichtangabe „Zweck". Medienroute, Vorschau-Signaturen und API-Resource kennen die Pfade nicht. Architekturtest: original_path und published_snapshot_path nur in Uploader, Snapshot-Service, Admin-Route, Archiv. S A.1, F.1
E.5 Legacy-Bereinigung (E-B11): press-release-images:purge-inactive-legacy (Standard Dry-Run, --execute): löscht Bildzeilen und Dateien für Legacy-Bilder (legacy_id gesetzt, rules_version NULL) an PMs mit Status rejected, archived oder draft; auf der Dev-Kopie 10.768 Bilder. Bericht mit Zahl je Status und Speicherplatz. Live-Schritt nach Deploy 1. Test mit Fixture-Zeilen. S keine
E.6 Korrekturweg (E-B7, DU S17): Admin ändert copyright und ai_declaration (Portal) mit Pflichtgrund; ai_declaration_source = admin_correction; Zwischenzustand „Bild vorläufig zurückgehalten" (Bild aus der öffentlichen Seite, Platzhalter aktiv) bis zur Entscheidung; Archiveintrag label_corrected mit alt/neu/Quelle/Grund/Bearbeiter (F.4). Kundenangabe und ursprüngliche Bestätigung bleiben unverändert. Kontaktweg im Formular und im Overlay. S A.1, F.4
E.7 Betrieb: config/images.php (Größen, Pixel, Timeout, Vertrauensliste für Phase 2); Zähler provenance_check_failed im Admin-Systemstatus; Laufzeit der Prüfung auf Live mit 16-MiB-Bild messen und im Protokoll notieren. S A.5

Block F — Beweisarchiv (DU Abschnitt 4)

Nr. Arbeitspaket Aufwand Abhängigkeit
F.1 Tabellen archive_entries (subject_type, subject_id, event Enum image_removed, image_replaced, pm_depublished, gdpr_change, label_corrected; actor_type, actor_id, reason_category Enum, payload_path, payload_hash, retain_until, legal_hold, legal_hold_note, legal_hold_set_by, legal_hold_set_at, deleted_at als Tombstone, created_at) und archive_access_log (entry_id, admin_id, accessed_at, purpose Pflicht). Append-only per Observer. Disk archive (storage/app/archive, außerhalb des Web-Roots), Payload als ZIP, verschlüsselt mit eigenem Schlüssel ARCHIVE_KEY aus der Umgebung über Illuminate\Encryption\Encrypter (nicht APP_KEY, damit ein Schlüsselwechsel der App das Archiv nicht berührt). retain_until = 31.12. des Ereignisjahres + 3 Jahre. M keine
F.2 App\Services\Archive\EvidenceArchive: record(event, subject, payload[], actor, reason) (packt, verschlüsselt, hasht, schreibt Eintrag), open(entry, admin, purpose) (entschlüsselt, protokolliert), export(entry) (signiertes ZIP mit Metadaten und Hash für den Anwalt), setLegalHold()/releaseLegalHold() (nur Admin, Pflichtvermerk; nach Aufhebung retain_until neu ab Aufhebungsdatum). Admin-Seite: Liste, Detail, Zugriff mit Zweck, legal_hold-Schalter, Export. Das Audit-Log der Anwendung referenziert nur archive_entries.id, nie Altwerte. M F.1
F.3 Jobs: archive:purge täglich (Einträge mit retain_until < heute und ohne legal_hold: Payload löschen, Tombstone setzen, Log ohne Inhalt); archive:export-tombstones täglich (JSON der Tombstone-IDs auf die Archiv-Disk, Teil der Sicherung); archive:reconcile-after-restore (liest den Export, entfernt Payloads zu Tombstones erneut; Bestandteil des Restore-Runbooks in dev/betrieb/deploy-runbook.md). Test: Purge im Dry-Run ändert nichts; legal_hold blockt; Reconcile löscht wiederhergestellte Payloads. S F.2
F.4 Anbindung (DU 4.1, S14): image_removed/image_replaced aus E.3 (Original, Snapshot, Bestätigung); pm_depublished in depublish(), archive(), changeStatusFromAdmin() weg von Published, deleteFromAdmin() einer veröffentlichten PM und Ende der Laufzeit (letzter veröffentlichter Stand: Text, Bilder-Snapshots, Kontaktfelder, Einreicher, Zeitpunkte, Grund als Kategorie); gdpr_change in AnonymizationService und CorrectionService (Diff alt/neu, Referenz auf Antrag); label_corrected aus E.6. Ablehnung im Freigabelauf: kein Eintrag (nur Vertragsdaten in der normalen DB). Test je Auslöser: Eintrag vorhanden, Payload entschlüsselbar, Hash stimmt. M F.2, E.2, E.3, E.6
F.5 DSE-Zusagen prüfen: Zugriffsprotokoll (F.1), Tombstone nach Restore (F.3) und die dokumentierte Interessenabwägung (DU 4.4 als Datei im Rechtsordner, Betreiber) müssen stehen, bevor DSE 9b in Deploy 3 veröffentlicht wird. Checkliste im Protokoll. S F.1 bis F.4

Block G — Zustimmungsgate (DU E12, S12)

Nr. Arbeitspaket Aufwand Abhängigkeit
G.1 Tabelle legal_document_acceptances (user_id, legal_document_id, type, version, accepted_at, context Enum submit, booking, api, gate, ip_hmac, user_agent). Insert-only. Bestehende booking_confirmations bleiben, werden nicht migriert. S keine
G.2 Gate vor jeder Einreichung: die vier Kunden-Submit-Pfade (create, edit, show, index) prüfen über einen gemeinsamen Trait, ob für AGB und Richtlinien in der aktuellen Version eine Annahme vorliegt; sonst Modal mit aktivem Klick je Dokument, Link auf den Text, kein „Weiternutzung gilt als Zustimmung". Bestandskunden werden beim nächsten Einreichen einmalig durchgeschleust. API (O2): POST /api/v1/legal/acceptances, submit antwortet ohne gültige Annahme mit 428 und required_versions. Test: Submit ohne Annahme blockiert, mit Annahme durch; neue Version fordert erneut. M G.1
G.3 Aktivierung erst in Deploy 3 mit AGB v0.7 (bis dahin hinter LEGAL_ACCEPTANCE_GATE=false), damit Bestandskunden gleich die neue Fassung annehmen und nicht zweimal klicken. S G.2

Block H — OpenGraph, Teaser, Beschnitt (DU E7, S15, S16)

Nr. Arbeitspaket Aufwand Abhängigkeit
H.1 Variante og (1200 × 630) mit eingebranntem Nachweisbalken unten: halbtransparenter Balken, Text „Foto: [Nachweis]" plus KI-Zusatz, per GD imagettftext mit resources/fonts/Inter_18pt-SemiBold.ttf (vorhanden, OFL 1.1, O4). Wird beim Upload und beim Korrekturweg neu erzeugt (Nachweis kann sich ändern). release-detail.blade.php:168 liefert og statt detail. Bestand ohne og-Variante: press-release-images:regenerate-variants --only=og für die 3.786 veröffentlichten Legacy-Bilder (Nachweis oder „Quelle: [Firma]"). Test: Variante existiert, Balken vorhanden (Farbprüfung der unteren Zeilen). M D.1, A.7
H.2 Teaser und Listen: alt und title der Vorschaubilder tragen den Nachweis („[Titel] · Foto: [Nachweis]"); Listen-Karten bekommen eine Bildunterschrift-Zeile mit dem Nachweis, wo Platz ist (Startseite, Kategorie, Newsroom, Suche, Archiv). Beide Portale. S D.1
H.3 Beschnitt-Steuerung (§ 9 Abs. 1 letzter Satz): Fokuspunkt je Bild (focus_x/focus_y in Prozent, Neun-Punkte-Wahl im Formular) oder no_crop (Letterbox mit neutralem Rand). resizeCover berücksichtigt beides für alle Varianten. Test: Fokus rechts oben schneidet links unten ab; no_crop zeigt das volle Bild. M A.7, B.1

Block I — Phase 2 und 3: C2PA, Metadaten, Partner

Nr. Arbeitspaket Aufwand Abhängigkeit
I.1 c2patool (Release-Binary, Version und Prüfsumme im Repo) in Sail-Image und per Ploi-Skript auf den Server; Aufruf über Symfony\Process mit Argumentliste, --detailed, 10 s, 1 MiB Ausgabe, keine externen Manifestverweise. Vertrauensliste config/c2pa.php (Aussteller; Geschäftsentscheidung). M E.7
I.2 Inspector-Erweiterung: validated_trusted, validated_untrusted, invalid, unsupported; nur das aktive Manifest zählt, Bestandteile werden nicht übernommen; Konflikt C2PA gegen XMP → C2PA gewinnt mit Vermerk; Aussteller nur aus dem Zertifikat. Testkorpus: OpenAI, Adobe Firefly, Google, Kamera mit Content Credentials, gebrochene Bindung, zwei Manifeste, Fotomontage mit KI-Bestandteil. M I.1
I.3 Sperre (DU E9): ai_declaration_locked nur bei validated_trusted mit trainedAlgorithmicMedia oder compositeWithTrainedAlgorithmicMedia; ai_declaration = max(Befund, Kundenangabe), ai_declaration_source = c2pa_verified; Höherstufung möglich; Unterschreitung verworfen und als lock_bypass_attempt geloggt (12 Monate, ohne Nutzdaten); API-Warnung ai_declaration_raised_by_metadata. Zustand A im Formular und Content-Credentials-Zeile im Overlay nur bei validated_trusted. S I.2
I.4 Messung auf Live (Laufzeit je Dateigröße, Fehlerquote nach vier Wochen). Revisit: mehr als eine Handvoll Widersprüche pro Monat → Korrekturweg neu bewerten. S I.3
I.5 Metadaten in die Varianten (DU S21): Imagick setImageProfile('xmp', …) mit dc:rights, photoshop:Credit, Iptc4xmpExt:DigitalSourceType für JPEG, PNG, WebP; nach dem Schreiben wieder auslesen; Variantenname mit Versionsanteil wegen max-age=86400. M D.1
I.6 Re-Sync-Sperre für Bilder (DU S22): sobald ein API-Partner Bilder liefert, dürfen Importer-Läufe entfernte oder korrigierte Bilder nicht wiederherstellen (analog lockSync() der PM). Erst bauen, wenn der Fall eintritt. S Partner

Block J — Ausblick: Bilder im Newsroom (Roadmap 1b.3)

Unverändert aus Fassung 1: Tabelle company_images mit denselben Spalten (gemeinsames Migrations-Makro), Logos ohne Kennzeichnung; Uploader und Bildmanager über HasImages verallgemeinert, Bestätigungen mit subject_type = Company; Newsroom-Bilder sind sofort öffentlich, deshalb Bestätigung vor dem Speichern; öffentliche Galerie mit Nachweiszeile und Overlay; Kontingent je Newsroom (Geschäftsentscheidung). Aufwand M. Voraussetzung: Deploy 1 und 2 live.


Block K — KI-Vorab-Check für Bilder (DU Abschnitt 12, E15, S24)

Grundlage ist die Text-Prüfstrecke: OpenAI über Http (Konfiguration services.openai), Manager/Treiber-Muster (ClassificationManager), deterministischer Fallback, unveränderliches ki_audits-Protokoll. Der Bild-Check bekommt denselben Aufbau mit eigenem Manager, eigenem Config-Block images.precheck und Bild-Eingabe (image_url mit Data-URI). Drei Regeln aus DU 12.2 gelten wörtlich: jeder Befund hat einen definierten Ausgang; öffentlich bleibt es „automatisierte Prüfung"; die KI setzt nie die Kennzeichnung.

Nr. Arbeitspaket Aufwand Abhängigkeit
K.1 ImagePrecheckManager mit Treibern openai und deterministic (liefert nie einen Befund); Vertrag check(string $absolutePath): ImagePrecheckResult. Vorbereitung als Datenminimierung: Imagick, 1024 px lange Kante, stripImage(), JPEG 82, Base64. Antwortschema DU 12.4 über response_format json_object; der Prompt fragt nur „Person(en) erkennbar", nie eine Identifizierung. Fehler, Timeout, unbrauchbare Antwort → Ergebnis error: durchlassen mit Vermerk, nie blockieren. Config: enabled (IMAGE_PRECHECK, Standard aus), stage (IMAGE_PRECHECK_STAGE, 1 = nur stock_watermark, 2 = alle Kategorien), provider, model (leer = services.openai.model), timeout 20 s, reject_threshold 0,85, queue_threshold 0,6. S A
K.2 Auswertung nach DU 12.3 in ImagePrecheckOutcome (reject, queue, hint, pass, error): reject bei stock_watermark, nudity_or_violence, document_or_id ab 0,85; queue bei screenshot_or_collage, third_party_logo_prominent, license_plate_readable ab 0,6 und bei den drei Ablehnungskategorien zwischen 0,6 und 0,85; hint bei persons_visible, bei looks_ai_generated nur wenn die Kundenangabe „ohne KI" lautet, und bei technical_defect; sonst pass. Stufe 1 wertet ausschließlich stock_watermark. ai_declaration bleibt unberührt (Regel 3). S K.1
K.3 Einbau in den Uploader: Check auf den Originalbytes vor dem Schreiben von Original und Derivaten. Formular: bereits in preflight() nach der Dateiauswahl, Ergebnis mit Hash-Bindung im Komponentenzustand; store() rechnet nur neu, wenn kein Ergebnis oder der Hash nicht passt. API: in store(). reject → ImageUploadException mit dem Grund in einem Satz (Formular unter dem Dateifeld, API 422 Feld image), nichts gespeichert, ki_audits-Eintrag image_precheck an der PM (Kategorie, Konfidenz, Modell, ohne Datei). Gespeicherte Bilder: Spalten precheck (JSON: Kategorien mit Konfidenz, Modell, Version, Zeitpunkt, Ausgang, Stufe), precheck_outcome, precheck_review_required_at, precheck_reviewed_at, precheck_reviewed_by, precheck_review_note; ki_audits-Eintrag je Check; precheck_snapshot als eigenes Feld im Bestätigungsprotokoll (DU 12.5); Archiv-Payload imageRemoved um die Spalten erweitert. M K.2
K.4 Admin-Queue: queue setzt precheck_review_required_at und withheld_at. Die Meldung bleibt im Freigabelauf (DU 12.3), nur das Bild ist bis zur Sichtung zurückgehalten (bestehender Zustand E.6, Platzhalter öffentlich). Kunde sieht „Wird kurz gesichtet" an der Titelbild-Karte. Admin-Entscheidung mit Vermerk: Freigabe (Bild wird sichtbar, OG-Variante bleibt) oder Ablehnung (Entfernen über PressReleaseImageRemover, Grund own_decision); beide als ki_audits-Eintrag image_precheck_review und in den precheck_reviewed_*-Spalten. M K.3
K.5 Oberfläche: Formular v2 zeigt Hinweise aus hint (Personen: Einverständnis-Satz aus DU 12.3; „wirkt KI-generiert" nur, wenn „ohne KI" gewählt ist; technischer Mangel als Notiz); Admin-PM-Seite bekommt den Sichtungsblock mit Befund, Freigabe/Ablehnung und Vermerk; Admin-PM-Liste Ansicht „Bild-Sichtung" mit Zähler; Systemstatus-Zähler offener Sichtungen. S K.4
K.6 Datenschutz und Betrieb (DU 12.6): AVV mit dem Anbieter und DSE-Absatz zur Bildsichtung gehören zu Deploy 3 (Betreiber, Anwalt); bis dahin bleibt der Check aus. Einschalten: IMAGE_PRECHECK=true, IMAGE_PRECHECK_STAGE=1 direkt nach Deploy 1b, Stufe 2 nach einer Woche ohne Fehlurteile. Wiedervorlage DU 12.8: Kategorie mit gehäuften Fehlurteilen per Config von reject auf queue herabstufen (images.precheck.downgrade_to_queue), nicht abschalten. S Betreiber

Nicht aufgenommen: Nachsichtung des Bestands; Bewerbung des Checks nach außen (würde den Hoster-Status berühren, DU 12.8); Sperre der Kennzeichnung aus dem Modellurteil (E9).


Reihenfolge und Deploys

Deploy Inhalt Live-Schritte Bedingung
1 Blöcke A bis E, D.6; Flag IMAGE_RIGHTS_V2=false migrate (Sekunden), Seeder LegalTextSeeder (v1, v2, v3 ohne Datum) und Richtlinien-Version, Backfill A.3 und E.1, press-release-images:purge-inactive-legacy erst Dry-Run dann --execute, .env: CONFIRMATION_IP_KEY, IMAGE_RIGHTS_V2=false keine
1b Flag einschalten IMAGE_RIGHTS_V2=true, config:cache; Legacy-Upload antwortet ab hier 410 Prüfung auf Live mit Testkonto. Wartet nicht auf den Anwalt (DU E14). Bestätigungstext img-confirm-v2 mit Verweis auf AGB 0.6.
2 Blöcke F, G (Gate aus), H migrate, .env: ARCHIVE_KEY, Disk archive anlegen, Scheduler (archive:purge, archive:export-tombstones), Restore-Runbook ergänzen, press-release-images:regenerate-variants --only=og parallel zur Anwaltsfrist
2b Block K (Check aus) migrate, .env: IMAGE_PRECHECK=false; nach AVV und DSE-Absatz: IMAGE_PRECHECK=true, IMAGE_PRECHECK_STAGE=1, später 2 Deploy 1b live; AVV mit dem Anbieter; DSE-Absatz DU 12.6 (Deploy 3)
3 AGB v0.7 (§ 8a, § 9), DSE v0.4 (9a, 9b, 13), img-confirm-v3 aktiv, Gate an Seeder legal_documents (AGB 0.7, DSE 0.4), legal_texts v3 effective_from, LEGAL_ACCEPTANCE_GATE=true, Interessenabwägung als Dokument abgelegt Anwaltsantwort, F.5
4 Block I c2patool auf dem Server, config/c2pa.php vier Wochen Deploy 1b ohne Auffälligkeiten, Testkorpus
5 Block J migrate Kontingententscheidung

Aufwand: Deploy 1 etwa zehn bis vierzehn Arbeitstage, Deploy 2 etwa acht bis zehn, Deploy 3 zwei, Deploy 4 fünf bis sieben.

Live-Schritte für Deploy 1 und 2 zusammen (Stand 22.09.2026, beide Blöcke sind gebaut):

  1. Dateien hochladen, php artisan view:clear && php artisan view:cache, php artisan config:cache.
  2. .env: IMAGE_RIGHTS_V2=false, CONFIRMATION_IP_KEY=<eigener Wert>, ARCHIVE_KEY=<php artisan archive:key>, LEGAL_ACCEPTANCE_GATE=false, IMAGE_PRECHECK=false; ARCHIVE_KEY getrennt sichern.
  3. php artisan migrate --force (neun Migrationen, alle additiv; press_release_images und press_releases ohne FULLTEXT, Sekunden).
  4. php artisan db:seed --class=LegalTextSeeder --force und php artisan db:seed --class=LegalDocumentSeeder --force (Richtlinien 0.2).
  5. php artisan image-rights:backfill-confirmations und php artisan press-releases:backfill-published-ever (je erst --dry-run).
  6. php artisan press-release-images:purge-inactive-legacy (Trockenlauf), dann mit --execute.
  7. php artisan queue:restart; Scheduler läuft archive:purge und archive:export-tombstones automatisch.
  8. Prüfung auf Live mit Testkonto (Flag noch aus: altes Formular sichtbar), dann IMAGE_RIGHTS_V2=true + config:cache (Deploy 1b). Ab dann antwortet der Legacy-Bildupload 410.
  9. Bestand: php artisan press-release-images:regenerate-variants --only=og --published (Trockenlauf), dann mit --execute — erzeugt die OG-Variante mit eingebranntem Nachweis für die 3.786 veröffentlichten Bestandsbilder; bis dahin fällt og:image auf die Detail-Variante zurück.
  10. Vorab-Check (Block K, Deploy 2b): erst nach AVV mit OpenAI und DSE-Absatz (DU 12.6) IMAGE_PRECHECK=true, IMAGE_PRECHECK_STAGE=1 + config:cache; nach einer Woche ohne Fehlurteile IMAGE_PRECHECK_STAGE=2. Nutzt OPENAI_API_KEY der Text-Prüfstrecke; das Modell muss Bilder verarbeiten können (IMAGE_PRECHECK_MODEL bei Bedarf). Offene Sichtungen: Pressemitteilungen ▸ Bild-Sichtung.

Abnahmetests

Bereich Fälle Block
Migration vorhandene Werte unverändert; 45 Bestätigungen mit v1-Verweis; published_ever_at-Backfill stimmt mit Status-Log überein A.1, A.3, E.1
Bereinigung Dry-Run ändert nichts; --execute entfernt nur Legacy-Bilder an rejected/archived/draft; veröffentlichte Legacy-Bilder bleiben E.5
Metadaten IPTC Credit, XMP trainedAlgorithmicMedia, algorithmicallyEnhanced ohne Vorschlag, PNG ohne Metadaten, WebP mit XMP, beschädigt → error A.5
Formular Pflichtfelder; Vorbelegung überschreibt keine Eingabe; Dateiwechsel setzt Schalter zurück; Zustand D speichert mit Vermerk; Kundenangabe und Portalkennzeichnung getrennt gespeichert B
Admin-Upload ohne Kundenklick keine Bestätigung und keine Veröffentlichung mit Bild; Magic-Link bestätigt mit Kontoinhaber; Bildtausch an veröffentlichter PM verlangt neue Bestätigung B.4, E.1
Atomarität DB-Fehler nach Dateispeicherung; parallele Titelbild-Uploads; Hash identisch in Original, Bestätigung, Prüfung A.6
Zugriff Original und Snapshot weder über Medienroute noch Vorschau noch API; Admin-Route mit Zweck im Zugriffslog E.4
API v1 Pflichtfelder 422, falsche Kennung 422 mit Hinweis, Statusgrenze, kompatible Response; Legacy 410 C
Bestätigung Wortlaut mit AGB-Fassung byte-genau rekonstruierbar; User, Token, Kanal korrekt; Firmenwechsel ändert alte Bestätigung nicht A.2, A.3
Lebenszyklus nie veröffentlicht → sofort alles weg; veröffentlicht → Archiveintrag, Derivate weg; Depublikation, Statuswechsel, Löschung, DSGVO-Änderung, Korrektur erzeugen je einen Eintrag; Purge, legal_hold, Reconcile E.3, F
Gate Submit ohne Annahme blockiert; mit Annahme durch; neue Version fordert erneut; API 428 G
Ausgabe beide Portale, Kunden-/Adminansicht, Vorschau, PDF; Nachweiszeile ohne Klick; Verantwortlich-Zeile nur mit Protokoll; Bestand „Quelle: [Firma]"; Overlay per Tastatur; OG-Variante mit Balken; Teaser-alt mit Nachweis; Fokus und no_crop D, H
Sprache keine „qualitätsgeprüft"/„Geprüfte Mitteilungen" mehr außerhalb der AGB; Richtlinien-Version hochgezählt D.6
Vorab-Check Stock-Wasserzeichen ab 0,85 lehnt ab, nichts gespeichert, Audit ohne Datei; 0,7 geht in die Queue mit zurückgehaltenem Bild; Personen → Hinweis, kein Eingriff; „wirkt KI" ändert die Kennzeichnung nie; Modellfehler lässt durch mit Vermerk; Stufe 1 ignoriert alle anderen Kategorien; Hash-Bindung zwischen Vorprüfung und Speichern; Freigabe macht sichtbar, Ablehnung entfernt; Snapshot im Protokoll und im Archiv-Payload; Check aus = keine Anfrage K
Phase 2 vertrauenswürdig signiert sperrt; untrusted nicht; gebrochene Bindung invalid; Bestandteil-Typ nicht übernommen; Höherstufung möglich, Unterschreitung geloggt I

Anwalt

Agenda, Prüfannahmen, finale Texte und Risiken stehen im Decision-Update (Abschnitte 3, 7, 8). Dieser Plan liefert dazu den Screenshot von Formular und Nachweiszeile nach Deploy 1b. Was der Anwalt kippen könnte, ist dort mit Reaktionen ohne Umbau belegt; der Plan hat dafür keine gesonderten Pakete.


Umsetzungsprotokoll

Datum Punkte Commit Bemerkung
22.09.2026 O4 / Schriften b624f80 resources/fonts eingecheckt (20 Dateien, OFL 1.1)
22.09.2026 A.1–A.8 e9bdff3 Migrationen, legal_texts (v1/v2/v3), image_rights_confirmations, ImageProvenanceInspector mit echten Fixtures, PressReleaseImageUploader, ImageService ohne Rückfall; 30 Tests
22.09.2026 B.1–B.5 40953d6 press-release-images-manager-v2 hinter IMAGE_RIGHTS_V2, Admin-Upload mit Magic-Link-Bestätigung, Remover; 11 Tests
22.09.2026 C.1–C.3 65ac72c v1-Pflichtfelder, Resource, Legacy 410, API-Doku de/en, Paritätsdokument; 7 Tests
22.09.2026 D.1–D.6 2124ffd ImageCredit, Overlay, Kunden-/Adminansicht, PDF, Richtlinien 0.2, Wortwahl; 5 Tests; Baseline bereinigt
22.09.2026 E.1–E.7 46b89e1 Gate, published_ever_at, Snapshot, Sofortlöschung (O1), Backfill, Legacy-Bereinigung (E-B11, 10.768 auf Dev), Beweiszugriff, Korrekturweg, Systemstatus; 14 Tests
22.09.2026 F.1–F.5 0240c14 archive_entries/archive_access_log, EvidenceArchive, PressReleaseArchiver, Hooks, Scheduler, Admin-Seite, Runbook; 9 Tests. F.5-Checkliste: Zugriffsprotokoll ✓, Restore-Abgleich ✓, Interessenabwägung als Dokument (Betreiber) offen
22.09.2026 G.1–G.3, H.1–H.3 62723b5 Zustimmungsgate (aus bis AGB 0.7, API 428/POST acceptances), OG-Balken, OG-Neuaufbau, Teaser-Nachweis; 8 Tests. Gesamtsuite nach Block E: 2.037 grün
22.09.2026 H.1 Bestand 3b613fa press-release-images:regenerate-variants --only=og, Trockenlauf Standard; Testhelfer umbenannt (Kollision mit ManualReviewGateTest)
22.09.2026 K.1–K.6 0e5cd20 ImagePrecheckManager (openai/deterministic), Auswertung 12.3, Uploader-Einbau mit Hash-Bindung, ki_audits image_precheck/image_precheck_review, Queue mit withheld_at, Admin-Sichtung (Liste, PM-Seite, Systemstatus), Formular-Hinweise, API 422/Warnung; 17 Tests. K.6 (AVV, DSE-Absatz, Einschalten) Betreiber
25.09.2026 Test auf Dev, Nacharbeiten 6d558cd, d99f55e KI-Badge am Artikelbild rechts unten (Überlappung mit Nachweis-Zeichen); Admin ▸ Management ▸ Bilder: Liste aller Titelbilder mit PM-Link, Nachweis, Bestätigung, KI-Check, Filter; 2 Tests
25.09.2026 Umsetzungsaudit 25.09. nachgearbeitet 4e34c3d, 48129e9 Block 8 im Audit-Entwicklungsplan: Freigabezustand isPubliclyDeliverable(), Ability confirmImageRights, Archiv-Sperren/Fehlerauswertung, archive_pending_at + Nachholer, Anonymisierung über den Remover, Rejected-Purge, App-Kopien fallen mit dem Tombstone; N09 offen
28.09.2026 Deploy 1, 2 und 2b live — Betreiber: 12 Migrationen (längste archive_pending_at 12 s), Seeder, Backfill 63 Bestätigungen / 35.459 published_ever_at, purge-inactive-legacy --execute 10.769 Bilder (~2,98 GB), purge-drafts 2 Entwürfe / 0 Abgelehnte (Altbestand korrekt geschützt), Archiv-Nachholer und Orphan-Sweep leer, vier Archiv-Tasks im Scheduler, OG-Varianten für 3.807 Bestandsbilder erzeugt, Caches neu
28.09.2026 Deploy 1b live — IMAGE_RIGHTS_V2=true; IMAGE_PRECHECK und LEGAL_ACCEPTANCE_GATE bleiben aus (AVV/DSE bzw. AGB 0.7). Betreiber hat den Bild-Upload mit dem neuen Formular über ein Testkonto auf Live geprüft: funktioniert. Legacy-Bildupload antwortet ab jetzt 410. Vier-Wochen-Frist für Block I (C2PA) läuft ab heute, also ab 26.10.2026
29.09.2026 Umsetzungsaudit 29.09. (Block 9 im Audit-Entwicklungsplan) 5ec252d (Bilder), 7cd84b2 (Archiv) Feed/Card-Helfer/PDF nur freigegebene Bilder (B-01/B-02), Livewire-Upload 16 MiB (B-03), Snapshot versioniert inkl. OG-Variante (N09), Bestätigung an Bild-ID/SHA gebunden (B-04), Entfernen unter Sperre (B-05), Korrektur über Archivauftrag (B-06/A-09), precheck_withheld_at (B-07), Magic-Link-Zweck image_confirmation (B-10), Sweep verwaister Originale (B-11). Korrektur zur Zeile vom 28.09.: „Altbestand korrekt geschützt" war nicht belegt — published_ever_at ist für alle abgelehnten Alt-Meldungen NULL (Backfill kann „offline genommen" nicht erkennen); purge-drafts griff nur nicht, weil die Bilder vorher entfernt waren. Jetzt schützt whereNull('legacy_id') bzw. PressRelease::wasEverPublished() den Altbestand ausdrücklich (A-05)