audit: Nacharbeit Umsetzungsaudit 25.09.2026 — Bildfreigabe, Beweisarchiv, KI-Atomarität, Berichte (Block 8, N01–N05, N07, N08, N10–N21, 5.1, 5.2)
Bilder (N01, N05, N07, N08): gemeinsamer Freigabezustand PressReleaseImage::isPubliclyDeliverable() für Seiten, OG und Medienroute (Bestand bleibt sichtbar, v2 nur mit Bestätigung, Admin-Upload bis zum Kundenklick nicht öffentlich); eigene Ability confirmImageRights nur für den Kontoinhaber, ohne Super-Admin-Kurzschluss, Identität und ausstehende Bestätigung auch im Service; Status-Regel unter der PM-Sperre; Cache-Flush nach dem Commit außerhalb der Dateibereinigung; Snapshot nach Bestätigung an veröffentlichter PM; Bestätigungstexte an die gezeigte Kennung gebunden. Beweisarchiv (N02, N10, N11, N12, N13, 5.1): Tombstone unter Zeilensperre mit erneuter Hold-/Fristprüfung; Dateioperationen ausgewertet, Commands mit FAILURE; record() über .tmp + move; Marker archive_pending_at mit archive:retry-pending und archive:sweep-orphans; Anonymisierung entfernt Bilder über den Remover mit Antragsbezug; Draft-Purge räumt Medien nie veröffentlichter abgelehnter Einreichungen; App-Kopien fallen mit dem Archiv-Tombstone; label_corrected nur für veröffentlichte Bilder; archive_entries.legal_request_id; FileDeletion-Helfer; Systemstatus-Checks. KI und Hintergrund (N03, N04, N14, N20, N21): Ergebnis-UPDATE und Audit in einer Transaktion; erwarteter Hash bis in reject()/holdForManualReview(); Ablaufmail per beanspruchtem Marker in einer Transaktion; strikte Schemaprüfung der Vision-Antwort; Fortschrittsmarke ungecacht. Berichte und Canonical (N15–N19, 5.2): lückenlose Alterskohorten; Feed-Identität mit Portal und Sprache; Sitemap-Rolle in der Kundenansicht; Indexverteilung auf einer Grundgesamtheit; Kopien nach Wegfall der Erstquelle neu bewertet; Kategorie-Admin leert Seiten-Caches; Dry-Run zählt alle Tabellen. Gesamtsuite: 2.122 Tests grün, einer übersprungen; Larastan und Pint sauber. Zwei Migrationen (nullable Spalten, INSTANT). N09 offen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
34fa342c7f
commit
2a2215b3c3
66 changed files with 3092 additions and 368 deletions
|
|
@ -88,6 +88,8 @@ class OpenAiImagePrecheckDriver implements ImagePrecheckDriver
|
|||
throw new RuntimeException('OpenAI-Bildsichtung lieferte ein unvollständiges Schema.');
|
||||
}
|
||||
|
||||
$this->assertSchema($decoded);
|
||||
|
||||
return ImagePrecheckResult::ok($decoded, 'openai', $model, null, [
|
||||
'prompt_version' => (string) config('images.precheck.prompt_version'),
|
||||
'sent_size' => [$prepared['width'], $prepared['height']],
|
||||
|
|
@ -95,6 +97,41 @@ class OpenAiImagePrecheckDriver implements ImagePrecheckDriver
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strikte Prüfung der Wertstruktur (Audit N20): Ein Schlüssel allein
|
||||
* reicht nicht — alle Kategorien auf `null`, leere Objekte, String-
|
||||
* Booleans („false" ist in PHP wahr) oder Konfidenzen außerhalb 0…1
|
||||
* ergäben sonst ein scheinbar belastbares `ok` ohne Treffer. Formal
|
||||
* unbrauchbare Antworten gehen als Ausnahme in den bestehenden
|
||||
* `error`-Pfad (Block K.1: durchlassen mit Vermerk, nie blockieren).
|
||||
*
|
||||
* @param array<string, mixed> $decoded
|
||||
*/
|
||||
private function assertSchema(array $decoded): void
|
||||
{
|
||||
foreach (ImagePrecheckCategory::cases() as $category) {
|
||||
$entry = $decoded[$category->value];
|
||||
|
||||
if (! is_array($entry)) {
|
||||
throw new RuntimeException("OpenAI-Bildsichtung lieferte ein ungültiges Schema: {$category->value} ist kein Objekt.");
|
||||
}
|
||||
|
||||
if (! array_key_exists('hit', $entry) || ! is_bool($entry['hit'])) {
|
||||
throw new RuntimeException("OpenAI-Bildsichtung lieferte ein ungültiges Schema: {$category->value}.hit ist kein Boolean.");
|
||||
}
|
||||
|
||||
$confidence = $entry['confidence'] ?? null;
|
||||
|
||||
if (! (is_int($confidence) || is_float($confidence)) || ! is_finite($confidence) || $confidence < 0 || $confidence > 1) {
|
||||
throw new RuntimeException("OpenAI-Bildsichtung lieferte ein ungültiges Schema: {$category->value}.confidence liegt nicht in 0…1.");
|
||||
}
|
||||
|
||||
if (array_key_exists('note', $entry) && $entry['note'] !== null && ! is_string($entry['note'])) {
|
||||
throw new RuntimeException("OpenAI-Bildsichtung lieferte ein ungültiges Schema: {$category->value}.note ist kein Text.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function systemPrompt(): string
|
||||
{
|
||||
$keys = implode(', ', array_map(fn (ImagePrecheckCategory $c): string => $c->value, ImagePrecheckCategory::cases()));
|
||||
|
|
|
|||
|
|
@ -124,7 +124,9 @@ final readonly class ImagePrecheckResult
|
|||
}
|
||||
|
||||
$confidence = isset($entry['confidence']) && is_numeric($entry['confidence']) ? (float) $entry['confidence'] : 0.0;
|
||||
$hit = (bool) ($entry['hit'] ?? false);
|
||||
// Nur ein echtes `true` zählt (Audit N20): der String "false" wäre
|
||||
// per Cast wahr. Die strikte Schemaprüfung sitzt im Treiber.
|
||||
$hit = ($entry['hit'] ?? false) === true;
|
||||
$note = isset($entry['note']) && is_string($entry['note']) && trim($entry['note']) !== '' ? mb_substr(trim($entry['note']), 0, 200) : null;
|
||||
|
||||
$normalized[$category->value] = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue