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):
- Dateien hochladen,
php artisan view:clear && php artisan view:cache, php artisan config:cache.
.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.
php artisan migrate --force (neun Migrationen, alle additiv; press_release_images und press_releases ohne FULLTEXT, Sekunden).
php artisan db:seed --class=LegalTextSeeder --force und php artisan db:seed --class=LegalDocumentSeeder --force (Richtlinien 0.2).
php artisan image-rights:backfill-confirmations und php artisan press-releases:backfill-published-ever (je erst --dry-run).
php artisan press-release-images:purge-inactive-legacy (Trockenlauf), dann mit --execute.
php artisan queue:restart; Scheduler läuft archive:purge und archive:export-tombstones automatisch.
- 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.
- 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.
- 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) |