import { createHash, randomUUID } from 'node:crypto'; import { existsSync } from 'node:fs'; import { copyFile, mkdir, readFile, rename, rm } from 'node:fs/promises'; import { basename, dirname, join, relative, resolve, sep } from 'node:path'; import { writeTextFileAtomic } from './atomic-write.js'; import { withCinemaFileLock } from './cinema-file-lock.js'; import { runDirectAiFilmMigration, validateDirectAiFilmImportReceipt, type DirectAiFilmImportReceiptArtifact, } from './cinema-direct-ai-film-migration.js'; import type { VideoMediaProbe } from './final-media.js'; import { stableCinemaJson } from './cinema-shot-compiler.js'; import { appendCinemaAttemptOutcome, appendCinemaGenerationAttempt, cinemaArtifactsDirFor, readCinemaGenerationLedger, } from './cinema-store.js'; import type { CinemaAttemptOutcome, CinemaGenerationAttempt, CinemaSha256 } from './cinema-types.js'; type JsonRecord = Record; export interface CinemaHistoricalReviewArtifact { schemaVersion: 1; reviewId: string; projectSlug: string; clipId: string; reviewedAt: string; sourceClassification: string; decision: 'historical-evidence-only'; technical: { sourceStatus: string; independentlyProbed: true; matchesSourceReceipt: true; probe: VideoMediaProbe; }; visual: { sourceStatus: string; checks: Record; contactSheetHash: CinemaSha256; endFrameHash: CinemaSha256; }; promotionAllowed: false; storyPurposeVerified: false; evidenceIds: CinemaSha256[]; providerCalls: 0; generationCalls: 0; spendAuthorized: false; contentHash: CinemaSha256; } export interface CinemaHistoricalImportReceiptArtifact { schemaVersion: 1; historicalImportId: string; projectSlug: string; clipId: string; importedAt: string; migrationId: string; migrationReceiptHash: CinemaSha256; source: { planHash: CinemaSha256; receiptHash: CinemaSha256; ledgerHash: CinemaSha256; imageHash: CinemaSha256; }; request: { prompt: string; durationSeconds: number; aspectRatio: string; resolution: string; generateAudio: boolean; mode: string; genre: string; sourceImageHash: CinemaSha256; }; provider: { provider: string; routeId: string; jobType: string; providerJobId: string; status: 'completed'; createdAt: string; }; cost: { currency: 'HIGGSFIELD_CREDITS'; quoted: number; actual: number; balanceBefore: number; balanceAfter: number; }; authorizationBoundary: { authorizationText: string; maximumJobs: 1; jobsSubmitted: 1; additionalJobsSubmitted: 0; fullFilmQueueAuthorized: false; fullFilmQueueUnchanged: true; historicalOnly: true; authorizesNewWork: false; }; media: { path: string; contentHash: CinemaSha256; byteLength: number; probe: VideoMediaProbe; }; review: { reviewPath: string; reviewHash: CinemaSha256; contactSheetPath: string; contactSheetHash: CinemaSha256; endFramePath: string; endFrameHash: CinemaSha256; }; lineage: { attemptId: string; outcomeId: string; payloadHash: CinemaSha256; executionHash: CinemaSha256; queueTaskId: string; promoted: false; }; mediaCopied: 3; providerCalls: 0; generationCalls: 0; spendAuthorized: false; executable: false; contentHash: CinemaSha256; } export interface CinemaHistoricalImportResult { created: boolean; receipt: CinemaHistoricalImportReceiptArtifact; review: CinemaHistoricalReviewArtifact; attempt: CinemaGenerationAttempt; outcome: CinemaAttemptOutcome; receiptPath: string; reviewPath: string; } export type CinemaHistoricalProbe = (path: string) => Promise; function sha256(value: string | Buffer): CinemaSha256 { return `sha256:${createHash('sha256').update(value).digest('hex')}`; } function artifactHash(value: unknown): CinemaSha256 { return sha256(stableCinemaJson(value)); } function withHash(value: T): T & { contentHash: CinemaSha256 } { return { ...structuredClone(value), contentHash: artifactHash(value) }; } function record(value: unknown, label: string): JsonRecord { if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must be an object`); return value as JsonRecord; } function text(value: unknown, label: string): string { if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} must be a non-empty string`); return value.trim(); } function number(value: unknown, label: string): number { if (typeof value !== 'number' || !Number.isFinite(value)) throw new Error(`${label} must be a finite number`); return value; } function boolean(value: unknown, label: string): boolean { if (typeof value !== 'boolean') throw new Error(`${label} must be boolean`); return value; } function cinemaHash(value: unknown, label: string): CinemaSha256 { const raw = text(value, label).toLowerCase(); const normalized = raw.startsWith('sha256:') ? raw : `sha256:${raw}`; if (!/^sha256:[a-f0-9]{64}$/.test(normalized)) throw new Error(`${label} must be SHA-256`); return normalized as CinemaSha256; } async function readJson(path: string, label: string): Promise { try { return record(JSON.parse(await readFile(path, 'utf8')), label); } catch (error) { throw new Error(`${label} is not readable JSON at ${path}: ${error instanceof Error ? error.message : String(error)}`); } } function resolveDeclaredPath(sourceRoot: string, declared: string, anchor: 'media' | 'reviews'): string { const normalized = declared.split('\\').join('/'); const marker = `${anchor}/`; const index = normalized.indexOf(marker); if (index < 0) throw new Error(`Historical source path must contain ${marker}: ${declared}`); const resolved = resolve(sourceRoot, normalized.slice(index)); const containment = relative(resolve(sourceRoot), resolved); if (!containment || containment === '..' || containment.startsWith(`..${sep}`)) throw new Error(`Historical source path escapes the source root: ${declared}`); return resolved; } async function hashExistingFile(path: string, expected: CinemaSha256 | null, label: string): Promise<{ contentHash: CinemaSha256; byteLength: number }> { if (!existsSync(path)) throw new Error(`${label} is missing at ${path}`); const bytes = await readFile(path); const contentHash = sha256(bytes); if (expected && contentHash !== expected) throw new Error(`${label} hash ${contentHash} does not match source receipt ${expected}`); return { contentHash, byteLength: bytes.byteLength }; } async function copyImmutable(source: string, target: string, expectedHash: CinemaSha256): Promise { await mkdir(dirname(target), { recursive: true }); if (existsSync(target)) { const existing = await hashExistingFile(target, expectedHash, 'existing historical evidence'); if (existing.contentHash === expectedHash) return; } const temporary = `${target}.tmp-${randomUUID()}`; try { await copyFile(source, temporary); await hashExistingFile(temporary, expectedHash, 'copied historical evidence'); await rename(temporary, target); } catch (error) { await rm(temporary, { force: true }); throw error; } } function assertProbeMatches(probe: VideoMediaProbe, technicalQa: JsonRecord, expectedBytes: number, requestedDuration: number): void { const expectedDuration = number(technicalQa.actual_duration_seconds, 'technical_qa.actual_duration_seconds'); const expectedWidth = number(technicalQa.width, 'technical_qa.width'); const expectedHeight = number(technicalQa.height, 'technical_qa.height'); const expectedCodec = text(technicalQa.codec, 'technical_qa.codec'); const expectedAudio = number(technicalQa.audio_streams, 'technical_qa.audio_streams') > 0; if (probe.durationSeconds === undefined || Math.abs(probe.durationSeconds - expectedDuration) > 0.05 || Math.abs(probe.durationSeconds - requestedDuration) > 0.1) throw new Error('Independent media duration does not match the historical receipt/request'); if (probe.width !== expectedWidth || probe.height !== expectedHeight || probe.videoCodec !== expectedCodec) throw new Error('Independent media video stream does not match the historical receipt'); if (probe.audioPresent !== expectedAudio) throw new Error('Independent media audio presence does not match the historical receipt'); if (probe.sizeBytes !== undefined && probe.sizeBytes !== expectedBytes) throw new Error('Independent media size does not match the historical receipt'); } function validateSourceContracts(plan: JsonRecord, sourceReceipt: JsonRecord, ledger: JsonRecord, clipId: string): void { if (text(plan.clip_id, 'plan.clip_id') !== clipId || text(sourceReceipt.clip_id, 'receipt.clip_id') !== clipId || text(ledger.clip_id, 'ledger.clip_id') !== clipId) throw new Error(`Historical source contracts do not agree on clip ${clipId}`); const scope = record(plan.scope, 'plan.scope'); const receiptScope = record(sourceReceipt.scope, 'receipt.scope'); const sourceReview = record(plan.review, 'plan.review'); if (scope.authorized_by_user !== true || number(scope.maximum_jobs, 'plan.scope.maximum_jobs') !== 1 || scope.full_film_queue_authorized !== false || sourceReview.submit_exactly_once !== true) throw new Error('Historical plan must record explicit user scope, exactly-once submission, one job, and no full-film authorization'); if (number(receiptScope.jobs_submitted, 'receipt.scope.jobs_submitted') !== 1 || number(receiptScope.additional_video_jobs_submitted, 'receipt.scope.additional_video_jobs_submitted') !== 0 || receiptScope.full_film_queue_unchanged !== true) throw new Error('Historical receipt must prove exactly one job and an unchanged full-film queue'); const route = record(plan.route, 'plan.route'); const billing = record(sourceReceipt.billing, 'receipt.billing'); const providerJob = record(sourceReceipt.provider_job, 'receipt.provider_job'); const localOutput = record(sourceReceipt.local_output, 'receipt.local_output'); if (text(providerJob.status, 'receipt.provider_job.status') !== 'completed' || text(ledger.status, 'ledger.status') !== 'completed') throw new Error('Historical source job and ledger must both be completed'); if (text(route.provider, 'plan.route.provider') !== text(providerJob.provider, 'receipt.provider_job.provider')) throw new Error('Historical provider identities disagree'); if (text(providerJob.id, 'receipt.provider_job.id') !== text(ledger.job_id, 'ledger.job_id')) throw new Error('Historical provider job IDs disagree'); if (text(providerJob.job_type, 'receipt.provider_job.job_type') !== text(ledger.job_type, 'ledger.job_type') || text(route.job_type, 'plan.route.job_type') !== text(ledger.job_type, 'ledger.job_type')) throw new Error('Historical job types disagree'); if (cinemaHash(localOutput.sha256, 'receipt.local_output.sha256') !== cinemaHash(ledger.sha256, 'ledger.sha256')) throw new Error('Historical media hashes disagree'); const quoted = number(route.quoted_credits, 'plan.route.quoted_credits'); const charged = number(billing.charged_credits, 'receipt.billing.charged_credits'); if (number(route.credit_cap, 'plan.route.credit_cap') !== quoted || number(billing.quoted_credits, 'receipt.billing.quoted_credits') !== quoted || charged > quoted || number(ledger.charged_credits, 'ledger.charged_credits') !== charged) throw new Error('Historical cost exceeds or disagrees with its exact one-job cap'); const before = number(billing.balance_before, 'receipt.billing.balance_before'); const after = number(billing.balance_after, 'receipt.billing.balance_after'); if (Math.abs((before - after) - charged) > 0.000001) throw new Error('Historical balance delta does not match the charged credits'); } function validateRequestAgreement(plan: JsonRecord, sourceReceipt: JsonRecord): void { const settings = record(plan.settings, 'plan.settings'); const request = record(sourceReceipt.request, 'receipt.request'); const pairs: Array<[unknown, unknown, string]> = [ [settings.duration, request.duration_seconds, 'duration'], [settings.aspect_ratio, request.aspect_ratio, 'aspect ratio'], [settings.resolution, request.resolution, 'resolution'], [settings.generate_audio, request.generate_audio, 'audio'], [settings.mode, request.mode, 'mode'], [settings.genre, request.genre, 'genre'], ]; for (const [left, right, label] of pairs) if (left !== right) throw new Error(`Historical plan/receipt ${label} disagree`); const source = record(plan.source, 'plan.source'); if (cinemaHash(source.sha256, 'plan.source.sha256') !== cinemaHash(request.source_sha256, 'receipt.request.source_sha256')) throw new Error('Historical source-image hashes disagree'); } function historicalDirectory(root: string, projectSlug: string, clipId: string): string { if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(clipId)) throw new Error(`Historical clip ID is not path-safe: ${clipId}`); return join(cinemaArtifactsDirFor(root, projectSlug), 'historical-attempts', clipId); } export function validateCinemaHistoricalReview(value: CinemaHistoricalReviewArtifact): void { const { contentHash, ...canonical } = value; if (contentHash !== artifactHash(canonical)) throw new Error('Cinema historical review content hash is invalid'); if (value.decision !== 'historical-evidence-only' || value.promotionAllowed || value.storyPurposeVerified || value.providerCalls !== 0 || value.generationCalls !== 0 || value.spendAuthorized) throw new Error('Cinema historical review cannot promote or authorize new work'); if (!value.technical.independentlyProbed || !value.technical.matchesSourceReceipt || value.evidenceIds.length < 3) throw new Error('Cinema historical review lacks independently reconciled evidence'); } export function validateCinemaHistoricalImportReceipt(value: CinemaHistoricalImportReceiptArtifact): void { const { contentHash, ...canonical } = value; if (contentHash !== artifactHash(canonical)) throw new Error('Cinema historical import receipt content hash is invalid'); if (value.authorizationBoundary.authorizesNewWork || value.authorizationBoundary.fullFilmQueueAuthorized || !value.authorizationBoundary.historicalOnly) throw new Error('Cinema historical import cannot broaden the source authorization boundary'); if (value.lineage.promoted || value.providerCalls !== 0 || value.generationCalls !== 0 || value.spendAuthorized || value.executable) throw new Error('Cinema historical import cannot promote, execute, call a provider, or authorize spend'); if (value.authorizationBoundary.maximumJobs !== 1 || value.authorizationBoundary.jobsSubmitted !== 1 || value.authorizationBoundary.additionalJobsSubmitted !== 0 || value.mediaCopied !== 3) throw new Error('Cinema historical import must remain one bounded job with exactly three selected evidence files'); } async function readMigrationReceipt(root: string, projectSlug: string, sourceRoot: string, generatedAt: string): Promise<{ migrationId: string; receipt: DirectAiFilmImportReceiptArtifact }> { const dryRun = await runDirectAiFilmMigration({ sourceRoot, projectSlug, workspaceRoot: root, generatedAt }); const path = join(cinemaArtifactsDirFor(root, projectSlug), 'migrations', dryRun.report.migrationId, 'receipt.json'); if (!existsSync(path)) throw new Error(`Historical import requires the matching metadata migration first: run video cinema-migrate --write for ${dryRun.report.migrationId}`); const receipt = JSON.parse(await readFile(path, 'utf8')) as DirectAiFilmImportReceiptArtifact; validateDirectAiFilmImportReceipt(receipt); if (receipt.sourceRootHash !== dryRun.report.sourceRootHash || receipt.projectSlug !== projectSlug) throw new Error('Historical import does not match the persisted Direct AI Film migration'); return { migrationId: dryRun.report.migrationId, receipt }; } export async function importCinemaHistoricalAttempt(input: { sourceRoot: string; projectSlug: string; workspaceRoot: string; clipId?: string; importedAt?: string; probe: CinemaHistoricalProbe; }): Promise { const sourceRoot = resolve(input.sourceRoot); const clipId = input.clipId ?? 'exploratory-clip-001'; const importedAt = input.importedAt ?? new Date().toISOString(); const migration = await readMigrationReceipt(input.workspaceRoot, input.projectSlug, sourceRoot, importedAt); const planPath = join(sourceRoot, 'generations', `${clipId}-plan.json`); const sourceReceiptPath = join(sourceRoot, 'generations', `${clipId}-receipt.json`); const sourceLedgerPath = join(sourceRoot, 'generations', 'exploratory-video-ledger.jsonl'); const [plan, sourceReceipt] = await Promise.all([readJson(planPath, 'historical plan'), readJson(sourceReceiptPath, 'historical receipt')]); const ledgerLines = (await readFile(sourceLedgerPath, 'utf8')).split(/\r?\n/).filter((line) => line.trim()).map((line) => record(JSON.parse(line), 'historical ledger row')); const sourceLedger = ledgerLines.find((row) => row.clip_id === clipId); if (!sourceLedger) throw new Error(`Historical ledger has no row for ${clipId}`); validateSourceContracts(plan, sourceReceipt, sourceLedger, clipId); validateRequestAgreement(plan, sourceReceipt); const source = record(plan.source, 'plan.source'); const localOutput = record(sourceReceipt.local_output, 'receipt.local_output'); const technicalQa = record(sourceReceipt.technical_qa, 'receipt.technical_qa'); const visualQa = record(sourceReceipt.visual_qa, 'receipt.visual_qa'); const sourceMediaPath = resolveDeclaredPath(sourceRoot, text(localOutput.path, 'receipt.local_output.path'), 'media'); const sourceImagePath = resolveDeclaredPath(sourceRoot, text(source.path, 'plan.source.path'), 'media'); const contactSheetSource = resolveDeclaredPath(sourceRoot, text(visualQa.contact_sheet, 'receipt.visual_qa.contact_sheet'), 'reviews'); const endFrameSource = resolveDeclaredPath(sourceRoot, text(visualQa.end_frame, 'receipt.visual_qa.end_frame'), 'reviews'); const expectedMediaHash = cinemaHash(localOutput.sha256, 'receipt.local_output.sha256'); const expectedImageHash = cinemaHash(source.sha256, 'plan.source.sha256'); const [planFile, receiptFile, ledgerFile, mediaFile, imageFile, contactSheetFile, endFrameFile] = await Promise.all([ hashExistingFile(planPath, null, 'historical plan'), hashExistingFile(sourceReceiptPath, null, 'historical receipt'), hashExistingFile(sourceLedgerPath, null, 'historical ledger'), hashExistingFile(sourceMediaPath, expectedMediaHash, 'historical media'), hashExistingFile(sourceImagePath, expectedImageHash, 'historical source image'), hashExistingFile(contactSheetSource, null, 'historical contact sheet'), hashExistingFile(endFrameSource, null, 'historical end frame'), ]); if (mediaFile.byteLength !== number(localOutput.size_bytes, 'receipt.local_output.size_bytes')) throw new Error('Historical media byte length does not match its source receipt'); const probe = await input.probe(sourceMediaPath); const request = record(sourceReceipt.request, 'receipt.request'); assertProbeMatches(probe, technicalQa, mediaFile.byteLength, number(request.duration_seconds, 'receipt.request.duration_seconds')); if (text(technicalQa.status, 'receipt.technical_qa.status') !== 'pass') throw new Error('Historical technical QA did not pass at source'); const directory = historicalDirectory(input.workspaceRoot, input.projectSlug, clipId); const receiptPath = join(directory, 'receipt.json'); const reviewPath = join(directory, 'review.json'); return withCinemaFileLock(receiptPath, async () => { if (existsSync(receiptPath)) { const receipt = JSON.parse(await readFile(receiptPath, 'utf8')) as CinemaHistoricalImportReceiptArtifact; const review = JSON.parse(await readFile(reviewPath, 'utf8')) as CinemaHistoricalReviewArtifact; validateCinemaHistoricalImportReceipt(receipt); validateCinemaHistoricalReview(review); if (receipt.migrationReceiptHash !== migration.receipt.contentHash || review.contentHash !== receipt.review.reviewHash) throw new Error('Historical import receipt points to changed migration or review evidence'); const ledger = await readCinemaGenerationLedger(input.workspaceRoot, input.projectSlug); const attempt = ledger?.attempts.find((item) => item.attemptId === receipt.lineage.attemptId); const outcome = ledger?.outcomes?.find((item) => item.outcomeId === receipt.lineage.outcomeId); if (!attempt || !outcome || attempt.payloadHash !== receipt.lineage.payloadHash || outcome.payloadHash !== receipt.lineage.payloadHash || outcome.executionHash !== receipt.lineage.executionHash || outcome.media.contentHash !== receipt.media.contentHash) throw new Error('Historical import receipt points to missing or changed Cinema lineage'); await hashExistingFile(receipt.media.path, receipt.media.contentHash, 'persisted historical media'); await hashExistingFile(receipt.review.contactSheetPath, receipt.review.contactSheetHash, 'persisted historical contact sheet'); await hashExistingFile(receipt.review.endFramePath, receipt.review.endFrameHash, 'persisted historical end frame'); return { created: false, receipt, review, attempt, outcome, receiptPath, reviewPath }; } const mediaPath = join(directory, 'media', basename(sourceMediaPath)); const contactSheetPath = join(directory, 'review-media', 'contact-sheet.png'); const endFramePath = join(directory, 'review-media', 'end-frame.png'); await copyImmutable(sourceMediaPath, mediaPath, mediaFile.contentHash); await copyImmutable(contactSheetSource, contactSheetPath, contactSheetFile.contentHash); await copyImmutable(endFrameSource, endFramePath, endFrameFile.contentHash); const checks = record(visualQa.checks, 'receipt.visual_qa.checks'); const visualChecks = Object.fromEntries(Object.entries(checks).map(([key, value]) => [key, boolean(value, `receipt.visual_qa.checks.${key}`)])); const evidenceIds = [mediaFile.contentHash, contactSheetFile.contentHash, endFrameFile.contentHash, planFile.contentHash, receiptFile.contentHash, ledgerFile.contentHash, imageFile.contentHash]; const reviewInput = { schemaVersion: 1 as const, reviewId: `historical-review-${clipId}`, projectSlug: input.projectSlug, clipId, reviewedAt: importedAt, sourceClassification: text(record(sourceReceipt.scope, 'receipt.scope').classification, 'receipt.scope.classification'), decision: 'historical-evidence-only' as const, technical: { sourceStatus: text(technicalQa.status, 'receipt.technical_qa.status'), independentlyProbed: true as const, matchesSourceReceipt: true as const, probe }, visual: { sourceStatus: text(visualQa.status, 'receipt.visual_qa.status'), checks: visualChecks, contactSheetHash: contactSheetFile.contentHash, endFrameHash: endFrameFile.contentHash, }, promotionAllowed: false as const, storyPurposeVerified: false as const, evidenceIds, providerCalls: 0 as const, generationCalls: 0 as const, spendAuthorized: false as const, }; const review = withHash(reviewInput); validateCinemaHistoricalReview(review); const settings = record(plan.settings, 'plan.settings'); const route = record(plan.route, 'plan.route'); const billing = record(sourceReceipt.billing, 'receipt.billing'); const providerJob = record(sourceReceipt.provider_job, 'receipt.provider_job'); const payload = { prompt: text(plan.prompt, 'plan.prompt'), durationSeconds: number(settings.duration, 'plan.settings.duration'), aspectRatio: text(settings.aspect_ratio, 'plan.settings.aspect_ratio'), resolution: text(settings.resolution, 'plan.settings.resolution'), generateAudio: boolean(settings.generate_audio, 'plan.settings.generate_audio'), mode: text(settings.mode, 'plan.settings.mode'), genre: text(settings.genre, 'plan.settings.genre'), sourceImageHash: imageFile.contentHash, }; const payloadHash = artifactHash(payload); const routeId = 'higgsfield-historical-import'; const providerJobId = text(providerJob.id, 'receipt.provider_job.id'); const executionHash = artifactHash({ historical: true, routeId, providerJobId, payloadHash }); const attemptId = `historical-attempt-${clipId}`; const outcomeId = `historical-outcome-${clipId}`; const shotId = `historical-${clipId}`; const queueTaskId = `historical-import-${clipId}`; const quoted = number(billing.quoted_credits, 'receipt.billing.quoted_credits'); const actual = number(billing.charged_credits, 'receipt.billing.charged_credits'); const attempt: CinemaGenerationAttempt = { attemptId, shotId, parentAttemptId: null, payloadHash, routeId, status: 'completed', preparedAt: text(providerJob.created_at, 'receipt.provider_job.created_at'), providerJobId, changedVariables: [], failureTags: [], evidenceIds, estimatedCost: { currency: 'HIGGSFIELD_CREDITS', amount: quoted }, actualCost: { currency: 'HIGGSFIELD_CREDITS', amount: actual }, }; const outcomeInput = { outcomeId, attemptId, shotId, queueTaskId, executionHash, payloadHash, status: 'completed' as const, observedAt: importedAt, providerJobId, media: { path: mediaPath, contentHash: mediaFile.contentHash, byteLength: mediaFile.byteLength }, actualCost: { currency: 'HIGGSFIELD_CREDITS', amount: actual }, evidenceIds, }; const outcome = withHash(outcomeInput); await appendCinemaGenerationAttempt(input.workspaceRoot, input.projectSlug, attempt, importedAt); await appendCinemaAttemptOutcome(input.workspaceRoot, input.projectSlug, outcome, importedAt); await mkdir(directory, { recursive: true }); await writeTextFileAtomic(reviewPath, `${JSON.stringify(review, null, 2)}\n`); const planScope = record(plan.scope, 'plan.scope'); const receiptScope = record(sourceReceipt.scope, 'receipt.scope'); const receiptInput = { schemaVersion: 1 as const, historicalImportId: `historical-import-${clipId}`, projectSlug: input.projectSlug, clipId, importedAt, migrationId: migration.migrationId, migrationReceiptHash: migration.receipt.contentHash, source: { planHash: planFile.contentHash, receiptHash: receiptFile.contentHash, ledgerHash: ledgerFile.contentHash, imageHash: imageFile.contentHash }, request: payload, provider: { provider: text(providerJob.provider, 'receipt.provider_job.provider'), routeId, jobType: text(providerJob.job_type, 'receipt.provider_job.job_type'), providerJobId, status: 'completed' as const, createdAt: text(providerJob.created_at, 'receipt.provider_job.created_at'), }, cost: { currency: 'HIGGSFIELD_CREDITS' as const, quoted, actual, balanceBefore: number(billing.balance_before, 'receipt.billing.balance_before'), balanceAfter: number(billing.balance_after, 'receipt.billing.balance_after'), }, authorizationBoundary: { authorizationText: text(planScope.authorization_text, 'plan.scope.authorization_text'), maximumJobs: 1 as const, jobsSubmitted: 1 as const, additionalJobsSubmitted: 0 as const, fullFilmQueueAuthorized: false as const, fullFilmQueueUnchanged: true as const, historicalOnly: true as const, authorizesNewWork: false as const, }, media: { path: mediaPath, contentHash: mediaFile.contentHash, byteLength: mediaFile.byteLength, probe }, review: { reviewPath, reviewHash: review.contentHash, contactSheetPath, contactSheetHash: contactSheetFile.contentHash, endFramePath, endFrameHash: endFrameFile.contentHash }, lineage: { attemptId, outcomeId, payloadHash, executionHash, queueTaskId, promoted: false as const }, mediaCopied: 3 as const, providerCalls: 0 as const, generationCalls: 0 as const, spendAuthorized: false as const, executable: false as const, }; const receipt = withHash(receiptInput); validateCinemaHistoricalImportReceipt(receipt); await writeTextFileAtomic(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`); return { created: true, receipt, review, attempt, outcome, receiptPath, reviewPath }; }); }