From 2cd20a0e72c73f6dec4f5825d6e6613be39fedb4 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sun, 31 May 2026 22:26:07 -0400 Subject: [PATCH] fix(growing): don't promote a growing file while its recorder is still recording The promotion worker promoted on mtime-idle (>=8s), but CIFS attribute caching makes an actively-growing MXF look idle, so it grabbed the live file ~15s into recording, uploaded it, flipped the asset live->ready, and unlinked it ("a worker is stealing the file"). Gate promotion on the recorder's live status: the growing asset's display_name is the recorder's current_session_id, so skip promotion while a recorder with that session is status='recording'. Only promote once recording has stopped. Co-Authored-By: Claude Opus 4.8 --- services/worker/src/workers/promotion.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/worker/src/workers/promotion.js b/services/worker/src/workers/promotion.js index 5ad63df..2f95692 100644 --- a/services/worker/src/workers/promotion.js +++ b/services/worker/src/workers/promotion.js @@ -146,6 +146,23 @@ async function promote(filePath) { } const asset = r.rows[0]; + // CRITICAL: do not promote while the recorder is STILL RECORDING this + // session. The mtime-idle heuristic is unreliable over CIFS (attribute + // caching makes an actively-growing MXF look "stable"), which caused the + // worker to grab a live file mid-record (~15s in), upload it, flip the + // asset to 'ready', and unlink it — "a worker is stealing the file". The + // growing asset's display_name IS the recorder's current_session_id, so + // gate on the recorder's live status: only promote once recording stopped. + const recActive = await query( + `SELECT 1 FROM recorders + WHERE current_session_id = $1 AND status = 'recording' LIMIT 1`, + [clipName] + ); + if (recActive.rows.length > 0) { + // Still recording — leave the growing file in place for the editor. + return; + } + const st = await stat(filePath); console.log(`[promotion] uploading ${rel} (${st.size} bytes) -> s3://${S3_BUCKET}/${s3Key}`); await uploadStreamToS3(S3_BUCKET, s3Key, createReadStream(filePath));