Files
vfxreview/lib/render-pipeline/config.ts
T
twotalesanimation f6a7f0ef5a feat(dashboard): show what is blocked and how the farm is doing
The dashboard was a generic task-manager view of a studio that does not work
that way. Two of its five headline numbers counted Task.dueDate, which no
pipeline code writes - the only writer is a manual field on the task detail
page, so those cards read zero by construction. The rest were unscoped and
cumulative ("approved" across every project ever finished), the activity feed
was the notification table, and the shot queue sampled 15 rows by updatedAt,
a column the farm touches constantly. Nothing about failed exports, QC depth
or the render farm appeared at all, so a RENDER_FAILED was only visible if
someone thought to open the queue and filter for it.

Replaced with pipeline state, which the system maintains on its own:

- Blocked queue: the four failed statuses plus exports that stopped
  progressing, each with the reason read off the actual cause - the failing
  validation check, the QC note, the aerender error, or the machine that
  claimed it and went quiet.
- Farm strip: machines, queue depth by job type, oldest unclaimed job, and
  worker-version drift, since a worker's version decides which fixes it has.
- Five counters that can go down, scoped to live productions, each linking to
  the rows it counted.

The queue page ignored ?status=, so the counters had nowhere to link; it now
takes an initial filter from the URL. Stat cards are hidden from CLIENT users
- the old ones were generic version counts, these are internal pipeline state.

Thresholds live in SystemConfig (dashboard.qcStaleHours, .stalledJobHours) so
they can be tuned without a redeploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 05:40:40 +02:00

122 lines
5.1 KiB
TypeScript

import { db } from "@/lib/db";
/**
* Pipeline-wide settings live in the existing SystemConfig key/value table
* (RenderPipeline2 §14.1) so the fleet can be tuned without redeploys.
*/
export const PIPELINE_CONFIG_DEFAULTS = {
"render.pollSeconds": 10,
"render.heartbeatSeconds": 30,
"render.leaseSeconds": 300,
"render.maxAttempts": 3,
"render.stallTimeoutSeconds": 600,
"render.urgentPriorityThreshold": 20,
/**
* How far a preview job outranks the render it follows. A preview is only
* created when its render finishes, so at equal priority it sorts behind
* every render queued alongside it — 10 shots run as 10 renders then 10
* previews, and nothing is reviewable until the end. A boost of 1 finishes
* shots end to end instead. Set to 0 for the old phase behaviour.
*/
"render.previewPriorityBoost": 1,
"render.outputRoot": "//SAN",
// Validation stage (§8). Disabling it restores the pre-Phase-3 flow
// (render → preview) without a redeploy, for a bad engine release.
"validation.enabled": "true",
"validation.sampleEvery": 25,
/** Per-check severity overrides, e.g. {"exr_type":"warn"} (§17.3). */
"validation.overrides": "{}",
/** Frames of head handle the plates carry; only used for the API timecode fallback (§8.1a). */
"validation.handleFrames": 8,
/**
* Studio-wide delivery defaults (§11.2). A project's own `deliveryConfig`
* column overrides these key by key; `{}` falls through to the built-ins in
* lib/render-pipeline/deliveries.ts.
*/
"delivery.defaultConfig": "{}",
"preview.maxWidth": 1920,
// Preview stage (§9): headless AE rebuild of the render with slate/burn-ins
"preview.enabled": "true",
"preview.templateAep": "X:/shared_projects_2026/UNGO_VFX/production/working_files/UNG_VFXOVERLAY_SLATE_TEMPLATE.aep",
"preview.templateComp": "UNG_EXPORT_TEMPLATE",
"preview.overlayComp": "UNG_VFX_OVERLAY",
"preview.lutComp": "_SHOW LUT",
"preview.movTemplate": "4444 Tri",
"preview.mp4Template": "REVIEW_PREVIEW",
// Essential Property names on the NETFLIX_SLATE layer for the per-submission
// fields. If a name is wrong the build logs the template's actual property
// names as a warning, so it is a config fix rather than a code change.
"preview.slateScopeProp": "Scope",
"preview.slateSubmissionProp": "Notes",
/**
* When the dashboard starts calling something stuck. An export waiting on QC
* is normal for a day and suspicious after two; an export in a live render
* state has either progressed or died long before four hours are up.
*/
"dashboard.qcStaleHours": 48,
"dashboard.stalledJobHours": 4,
} as const;
export type PipelineConfigKey = keyof typeof PIPELINE_CONFIG_DEFAULTS;
export async function getConfigValue(key: PipelineConfigKey): Promise<string> {
const row = await db.systemConfig.findUnique({ where: { key } });
return row?.value ?? String(PIPELINE_CONFIG_DEFAULTS[key]);
}
export async function getConfigNumber(key: PipelineConfigKey): Promise<number> {
const raw = await getConfigValue(key);
const n = Number(raw);
return Number.isFinite(n) ? n : Number(PIPELINE_CONFIG_DEFAULTS[key]);
}
export async function getConfigBoolean(key: PipelineConfigKey): Promise<boolean> {
const raw = (await getConfigValue(key)).trim().toLowerCase();
return raw === "true" || raw === "1" || raw === "yes";
}
/** Preview-stage settings handed to the worker inside the PREVIEW_ONLY manifest. */
export async function getPreviewConfig() {
const [
enabled, templateAep, templateComp, overlayComp, lutComp, movTemplate, mp4Template,
slateScopeProp, slateSubmissionProp,
] = await Promise.all([
getConfigBoolean("preview.enabled"),
getConfigValue("preview.templateAep"),
getConfigValue("preview.templateComp"),
getConfigValue("preview.overlayComp"),
getConfigValue("preview.lutComp"),
getConfigValue("preview.movTemplate"),
getConfigValue("preview.mp4Template"),
getConfigValue("preview.slateScopeProp"),
getConfigValue("preview.slateSubmissionProp"),
]);
return {
enabled, templateAep, templateComp, overlayComp, lutComp, movTemplate, mp4Template,
slateScopeProp, slateSubmissionProp,
};
}
/** Worker-facing config bundle returned by E6 registration. */
export async function getWorkerConfig() {
const [
pollSeconds, heartbeatSeconds, leaseSeconds, maxAttempts, stallTimeoutSeconds,
validationEnabled, validationSampleEvery, validationHandleFrames,
] = await Promise.all([
getConfigNumber("render.pollSeconds"),
getConfigNumber("render.heartbeatSeconds"),
getConfigNumber("render.leaseSeconds"),
getConfigNumber("render.maxAttempts"),
getConfigNumber("render.stallTimeoutSeconds"),
getConfigBoolean("validation.enabled"),
getConfigNumber("validation.sampleEvery"),
getConfigNumber("validation.handleFrames"),
]);
return {
pollSeconds, heartbeatSeconds, leaseSeconds, maxAttempts, stallTimeoutSeconds,
// Whether the validation stage runs is the server's call, so switching it
// off is a config change here rather than a redeploy of every worker (§8).
validationEnabled, validationSampleEvery, validationHandleFrames,
};
}