feat(validation): manual override for a failed run
Deploy / deploy (push) Successful in 6m13s

Checks fail on renders that are genuinely fine — a plate the engine
cannot reach, an expectation that does not apply to this shot — and a
VALIDATION_FAILED export was a dead end, blocking delivery on a shot a
human can see is correct.

Override validation on the Export detail page resumes the pipeline where
a pass would have handed off: preview, then READY_FOR_QC. It never skips
QC and never delivers directly, so the frames are still looked at — the
override waives the automated check, not the human one.

The failed ValidationResult rows are left untouched rather than rewritten,
so the QC queue keeps listing them as warnings and whoever QCs the shot
sees what was waved through. The reason is required and recorded with the
checks it covered on the export's timeline, and the validation panel
keeps an "overridden" badge long after the event scrolls away.

Restricted to admin/producer/supervisor: waving a failed check through is
a supervisor's call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
twotalesanimation
2026-08-14 11:19:45 +02:00
parent af810258e6
commit e66e4868ea
6 changed files with 236 additions and 3 deletions
@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from "next/server";
import { toErrorResponse } from "@/lib/render-pipeline/errors";
import { requirePipelineAdmin } from "@/lib/render-pipeline/session-auth";
import { overrideValidation, overrideValidationSchema } from "@/lib/render-pipeline/validation";
// ── POST /api/render/exports/{exportId}/override-validation ──────────────────
//
// A human overruling a failed validation run. Resumes the pipeline at the
// preview stage, so the export still goes through QC; the failed checks stay on
// record and keep showing up as warnings in the QC queue.
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ exportId: string }> }
) {
try {
// Waving a failed check through is a supervisor's call, not any user's.
const user = await requirePipelineAdmin();
const { exportId } = await params;
let raw: unknown;
try {
raw = await req.json();
} catch {
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
}
const parsed = overrideValidationSchema.safeParse(raw);
if (!parsed.success) {
return NextResponse.json({ error: "A reason is required", details: parsed.error.issues }, { status: 422 });
}
const result = await overrideValidation(exportId, {
reason: parsed.data.reason,
userId: user.id,
userName: user.name ?? user.email ?? null,
});
return NextResponse.json(result);
} catch (err) {
const { body, status } = toErrorResponse(err);
return NextResponse.json(body, { status });
}
}