import type { CinemaProductionQueueArtifact } from './cinema-queue-types.js'; import { enqueueCinemaTask, readCinemaProductionQueue } from './cinema-production-queue.js'; import { writeCinemaCharacterSheet, writeCinemaReferencePack, writeCinemaShotManifest, } from './cinema-store.js'; import type { CinemaWalkingSkeletonBundle } from './cinema-types.js'; import { validateCinemaWalkingSkeletonBundle } from './cinema-validation.js'; export async function enqueueCinemaWalkingSkeletonDryRun( root: string, bundle: CinemaWalkingSkeletonBundle, createdAt = new Date().toISOString(), ): Promise { const validation = validateCinemaWalkingSkeletonBundle(bundle); if (!validation.ok) { const detail = validation.issues.slice(0, 8).map((issue) => `${issue.path}: ${issue.message}`).join('; '); throw new Error(`Cinema walking skeleton is invalid: ${detail}`); } await Promise.all(bundle.characterSheets.map((sheet) => writeCinemaCharacterSheet(root, sheet))); await Promise.all(bundle.referencePacks.map((pack) => writeCinemaReferencePack(root, pack))); await Promise.all(bundle.shotManifests.map((manifest) => writeCinemaShotManifest(root, manifest))); for (const manifest of [...bundle.shotManifests].sort((left, right) => left.ordinal - right.ordinal)) { const generateTaskId = `cinema-${manifest.shotId}-generate`; const visualQcTaskId = `cinema-${manifest.shotId}-visual-qc`; await enqueueCinemaTask(root, manifest.projectSlug, { taskId: generateTaskId, kind: 'video-generate', payload: { artifactIds: [manifest.shotId, manifest.referencePackId], parameters: { dryRun: true, compiledPromptHash: manifest.compiledPrompt.contentHash, providerParametersHash: manifest.providerParameters.contentHash, durationSeconds: manifest.durationSeconds, }, }, routeId: 'fixture-dry-run', shotId: manifest.shotId, estimatedCost: { currency: 'USD', amount: 0 }, createdAt, }); await enqueueCinemaTask(root, manifest.projectSlug, { taskId: visualQcTaskId, kind: 'visual-qc', payload: { artifactIds: [manifest.shotId, generateTaskId], parameters: { dryRun: true, lanes: ['technical', 'identity', 'continuity'] }, }, dependencies: [generateTaskId], routeId: 'fixture-dry-run', shotId: manifest.shotId, createdAt, }); await enqueueCinemaTask(root, manifest.projectSlug, { taskId: `cinema-${manifest.shotId}-editorial-qc`, kind: 'editorial-qc', payload: { artifactIds: [manifest.shotId, visualQcTaskId], parameters: { dryRun: true, beatId: manifest.story.beatId, scenePurpose: manifest.story.scenePurpose, expectedChange: manifest.story.expectedChange, }, }, dependencies: [visualQcTaskId], routeId: 'fixture-dry-run', shotId: manifest.shotId, createdAt, }); } const queue = await readCinemaProductionQueue(root, bundle.ledger.projectSlug); if (!queue) throw new Error('Cinema walking-skeleton queue was not persisted'); return queue; }