b151e8cc521e55ffcc2f62f6ed3965b59b3cc50e
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
53fc8dc65b |
fix(pipeline): re-exports keep a shot's approval, and carry no client-facing text
Deploy / deploy (push) Successful in 6m1s
A shot is routinely re-exported for delivery after it has already been client-approved — most often an unscaled/un-punched-in pass. Every pipeline-created Version defaulted to approvalStatus PENDING_REVIEW, which is exactly what the dashboard counts, the shot page badge and the playlist read as "awaiting review" — so a re-export made an approved shot look like new unreviewed work to the whole team, with nothing actually different about its approval. approvalStatus is now inherited from the task's previous latest version rather than left at the create default. A task with no prior version (its first export) still gets PENDING_REVIEW, which is correct. Separately, every pipeline version was stamped with notes: "Pipeline render vXXX (internal only)" — and notes is the artist's message to reviewers, rendered verbatim in the client portal and the review player. Nothing enforces that a pipeline version stays unshared once created (see the gap noted in RenderPipeline2.md), so that internal-sounding string could reach a client if the shot were re-shared after a re-export. notes is left unset; what a version is and where it came from belongs on the Export and its timeline, which is where a human would actually go looking. RenderPipeline2.md §9 documents both, plus the share-flow gap that let notes reach a client in the first place: shareWithClient promotes whichever Version is isLatest with no awareness that a pipeline version is meant to stay internal permanently. Not fixed here — closing it needs a way to identify a pipeline-origin version at the share call sites, and Export.versionId is currently a one-way scalar with no inverse relation on Version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
483db65eb3 |
fix(connector): build shots at the shot's frame rate, not a hardcoded 24
Deploy / deploy (push) Successful in 6m23s
Shot.fps existed, the lookup API returned it, and one button honoured it — Set Start Timecode — while the shot builder ignored it entirely. Comps were created at 24 and every plate conformed to 24. DH2 is a 25 fps show. Its plates carry framesPerSecond 25/1, so comps built at 24 conform every plate to the wrong speed and compute every frame-from-timecode sum against the wrong rate. The builder now takes the rate from the shot record, which is the show's delivery rate and therefore the authority; a plate's own rate is whatever happens to be on disk. Import Renders follows the comp it imports into, and Import EXR follows the shot. Validation gains plate_fps, comparing the render against the timing plate. timecode_fps only compares the render to its manifest and both come from the comp, so a shot built at the wrong rate agrees with itself and passes; the plate is the independent witness. It is a WARN, never a FAIL: sampling DH2 found one plate at 50 fps against a 25 fps show, a deliberate over-crank, so a whole-number multiple is reported as exactly that and anything else as a probable rate mistake. Note this does not fix the data: all 175 DH2 shots still carry fps 24 from the create-time default and need correcting before their comps are rebuilt. Panel 2.4.0, worker 1.4.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c9b0f25bb2 |
fix(pipeline): let a finished render's preview outrank pending renders
Deploy / deploy (push) Successful in 6m11s
Urgent already propagated — a preview inherits its render's priority, so an urgent export stays ahead all the way to QC. The complaint was the other half: at equal priority previews always ran last. The queue orders by priority then age, and a preview job is only created when its render finishes, so it is always newer than the renders queued beside it. Ten shots ran as ten renders and then ten previews, and nothing was reviewable until the end of the batch. render.previewPriorityBoost (default 1) subtracts from the render's priority when the preview is created, so a shot finishes end to end before the next render starts. The boost never promotes a job across the urgent threshold: a normal preview must not inherit permission to run outside the render window that its render never had. Set it to 0 for the old behaviour. Worth knowing separately: a worker will not claim preview jobs at all while an artist has AE open on that machine, which during the day is the larger reason previews appear to wait. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4642ecb11f |
feat(pipeline): skip the preview build and inherit the previous one
Deploy / deploy (push) Successful in 7m8s
Some exports are deliberately framed differently from their review — unscaled or un-punched-in EXRs — and a preview rebuilt from those frames would be the wrong picture. The previous export's MOV is the correct one. skipPreview renders the EXRs and goes straight to READY_FOR_QC. Available at Queue Export (a "No preview" checkbox in the panel, E1 field) and as an option when overriding a validation failure. The inheritance is as important as the skip: deliveryMovPath, the review MP4, the thumbnail and the Version are carried over from the newest earlier export of that shot which has them. Without it the delivery builder — which takes its MOV from deliveryMovPath — would package EXRs with no MOV at all. Where there is nothing to inherit, the export still reaches QC and the timeline says it has no review media. QC is untouched by any of this: a skipped preview still needs a human pass before the export can be delivered. Panel 2.3.0. Adds migration 20260814000000_export_skip_preview. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e66e4868ea |
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> |
||
|
|
991abe072c |
fix(validation): anchor the timecode check on a delivered plate
Deploy / deploy (push) Successful in 6m12s
UNG_110_030_010 v002 failed with a 461614-frame offset on a render whose timecode was correct. Two causes, both in how the plate reference was recorded. The comp held three same-length elements and the timing plate was picked by duration alone, so a PRERENDER won on layer order. Using a pre-render as the timecode reference is circular anyway: it was rendered from this comp, so it can only ever agree with it — including when both are wrong. Only footage under the Footage Root is now eligible; among those, longest still wins. The offset itself came from the plate's frameStart. AE writes renders as "..._PRERENDER_01001.exr" while delivered plates use "....01001.exr", and the parser only handled the dot form, so the number fell back to the comp's frame range — 462615 against a render numbered from 1001. Both separators now parse, and an unparseable name omits the field rather than filling it with a number from the wrong coordinate system. Verified against the real manifest: the BG01 plate is now chosen over both pre-renders, and all three filenames parse to 1001. Panel 2.2.1, worker 1.2.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
37f73a2bd7 |
fix(validation): separate output file numbering from comp frame numbers
Deploy / deploy (push) Successful in 6m13s
A render carries frame numbers in two unrelated systems. The comp is anchored to its plate's timecode, so its timeline runs 1247792-1247896; the output module names files from 1001. Validation compared the file numbers against the comp range and failed every correctly-rendered shot by ~1.2 million frames — first_frame, last_frame and missing_frames all reporting a defect that was not there. The manifest now carries outputFrameStart (the output module's starting number, house convention 1001) alongside the comp range. Count and contiguity are measured on the output numbering; first/last compare against outputFrameStart and are SKIPPED on older manifests, where the count and gap checks still hold on their own. The same conflation broke the API timecode fallback: it anchored on the comp's frameStart while the render's first frame is 1001, producing a +1246791 frame "offset". Shot.timecodeStart is the edit in-point and the plate carries handleFrames of head handle, so the reference is the API value less handles, anchored on the render's own first frame. Verified on the shot that reported it: UNG_107_051_010 v004 now passes count, contiguity and a plate-anchored timecode match. Regression tests cover both coordinate systems, including that a genuine +8 handle offset still fails. Panel 2.1.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
feb1c7b3e9 |
feat(render-pipeline): validation, QC and delivery builder
Completes RenderPipeline2 phases 3-5. Additive throughout: with validation.enabled=false the pipeline behaves exactly as before. Validation (§8) runs on the worker straight after a render, while it still holds the job and the files are local. Everything is read from the EXR header — one 64 KB read per sampled frame, no pixel decode — so the engine needs no oiiotool. The render job deliberately stays open through the stage, so a worker that dies there is reaped and retried instead of stranding the export in VALIDATING. Timecode is the primary check and is anchored on the plate, never the API: measured on the live show, Shot.timecodeStart is the editorial in-point and sits one handle-length (8f) ahead of the plate, so checking against it would pass the defective exports and fail the correct ones. Reference precedence is manifest timing plate -> plate found on disk -> API value, and the API rank is WARN-only. The panel now resolves plate provenance by walking the comp's own footage layers, so the worker never guesses which element carries the comp's timing. Verified against real studio renders via the new --validate mode: it catches both defect classes seen in shipped work — the +8 handle offset and frame-derived timecode (00:00:41:17 = frame 1001 at 24fps). QC (§10) is one service behind two front doors, API-key for the panel and session for the web. PASS means ready to upload and nothing else; FAIL sets the comp task to CHANGES. The panel gains a QC section that imports the real EXR sequence at full res rather than the H.264. Delivery (§11) rides the existing queue as a DELIVERY_BUILD job: hard links with a copy fallback, verification by re-hashing delivered bytes against the validation-time digest, manifest.json and the CSV report. fsutil hardlink list is unavailable over SMB, so link identity is never queried — reading the bytes back covers links and copies alike. Also fixes ApiClient.ProgressAsync throwing on transport failure, which would have aborted work in flight during a brief server outage; progress reporting is best-effort by contract. Removes the stale RenderWorker/ rule from the root .gitignore. The worker's source is tracked like the rest of the repo and its own .gitignore already covers bin/, obj/ and publish/; the rule silently excluded new files while the existing 20 stayed tracked. Not yet applied: migration 20260813000000 was hand-written because the dev database was unavailable — run prisma migrate deploy and verify before relying on it. None of this has run against a live server. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cc89415a29 |
feat(pipeline): render queue, worker service and automated preview generation
One "Queue Export" click now renders the EXR sequence, then rebuilds the shot headlessly with the studio slate/overlay template to produce the delivery MOV and review MP4. Implements RenderPipeline2 phases 1-2 plus the preview stage. Server: - New models Export, RenderJob, ExportEvent, Machine, WorkerHeartbeat, plus Project.deliveryConfig and per-submission slate fields (Export.vfxScope, Export.submissionNote, inherited from the shot's previous export). Both migrations are purely additive; no existing column is touched. - lib/render-pipeline: server-enforced state machine, transactional version increment with supersede, atomic FOR UPDATE SKIP LOCKED claim gated by machine availability windows, and a lease reaper run from instrumentation.ts. - /api/ext/* endpoints for the panel and workers; session-auth mirrors under /api/render and /api/machines for the web UI. - Pipeline pages: render queue, export detail, machine monitoring, plus an Exports tab on shot detail. RenderWorker (.NET 8 Windows service, new): - Registration, heartbeat as cancel channel, claim loop, aerender runner with progress parsing and stall watchdog, crash recovery and disk-spooled reporting that survives server downtime. - Preview stage: headless AE assembles the preview comp into a throwaway AEP with both output modules queued, then a single aerender pass renders them. Preview jobs are not claimed while an interactive AE session is open, so an artist's project is never taken over. AE panel: Queue Export with live status polling, urgent flag, retry, and the VFX Scope / Submission Note fields. Every existing panel action is unchanged. Preview chaining ships disabled behind SystemConfig preview.enabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |