/** The expected/standard keyframe size; a uniform non-standard set is advisory-only. */ export declare const STANDARD_KEYFRAME_DIMENSIONS = "1280x720"; /** * Dimensions from a JPEG's first SOF marker. * * PNG-only was not a simplification, it was a blind spot: this gate hardcoded * `references/scene-keyframe.png`, while the 3d-animation-short skill writes * `references/kf-s01.jpg`. The gate therefore could not see a single keyframe of * any film that skill produced — it would have reported all eight scenes missing * — which is why that skill hand-rolled its own inline keyframe check instead of * calling this one. A shared gate nobody can call is not a gate. * * Walks the marker chain rather than scanning for bytes, so an SOF-looking pair * inside EXIF or a thumbnail cannot be mistaken for the real frame header. */ export declare function parseJpegDimensions(bytes: Uint8Array): { width: number; height: number; } | null; /** Dimensions of a PNG or JPEG, whichever the bytes turn out to be. */ export declare function parseImageDimensions(bytes: Uint8Array): { width: number; height: number; } | null; /** Machine-readable finding codes emitted by the gate. */ export type KeyframeQcFindingCode = 'storyboard-missing' | 'keyframe-missing' | 'keyframe-unreadable' | 'keyframe-dimension-mismatch' | 'keyframe-nonstandard-size' | 'keyframe-cast-missing' | 'keyframe-cast-unverified'; export interface KeyframeQcFinding { code: KeyframeQcFindingCode; /** 'error' findings fail the gate; 'advisory' findings never do. */ severity: 'error' | 'advisory'; /** Present for per-scene findings (missing/unreadable/mismatched keyframes). */ sceneIndex?: number; /** Absolute path of the file the finding is about, when file-scoped. */ path?: string; detail: string; } export interface KeyframeQcReport { schemaVersion: 1; projectSlug: string; generatedAt: string; /** Scene count read from artifacts/storyboard.json (0 when the storyboard is missing). */ sceneCount: number; /** Number of scene-keyframe.png files present on disk (readable or not). */ keyframeCount: number; /** The dominant (most common) WxH among readable keyframes, e.g. "1280x720"; null when none. */ dominantDimensions: string | null; findings: KeyframeQcFinding[]; /** 'fail' iff any severity 'error' finding is present. */ status: 'pass' | 'fail'; } /** * Parse width/height from a PNG file's leading bytes (signature + IHDR chunk). * Pure and dependency-free: width is bytes 16-19 big-endian, height 20-23. * Returns null for short buffers, a bad PNG signature, a non-IHDR first chunk, * or zero dimensions (all invalid per the PNG spec). */ export declare function parsePngDimensions(bytes: Uint8Array): { width: number; height: number; } | null; /** Expected on-disk keyframe path for a scene: projects//references/scene-keyframe.png. */ export declare function keyframePathFor(workspaceRoot: string, projectSlug: string, sceneIndex: number): string; /** * Where scene `i`'s keyframe actually is, according to the ASSET MANIFEST — * falling back to the legacy `references/scene-keyframe.png` name. * * The manifest is what `buildExecutionPayload` reads when it decides which * image becomes the start frame, so it is the only answer that matches what * will really be submitted. Checking a hardcoded filename instead meant this * gate silently examined nothing on every project that names its keyframes * differently — and then reported a clean pass on the file count it did find. */ export declare function resolveKeyframePath(projectDir: string, manifest: { assets?: Array<{ kind?: string; sceneIndex?: number; path?: string; }>; } | null, sceneIndex: number): string | null; /** One character the gate asks the vision model to find in a keyframe. */ export interface KeyframeCastSubject { name: string; /** Visual descriptor from characters.json / the show bible; may be empty. */ description: string; } /** Injectable so the cast check is offline-testable and provider-agnostic. */ export interface KeyframeCastVisionClient { /** True when `subject` is visibly present in the image at `imagePath`. */ isPresent(imagePath: string, subject: KeyframeCastSubject): Promise; } /** * The question put to the vision model, one character at a time. * * Deliberately asks only "is this character VISIBLE", not "does it match the * reference" — identity drift is consistency-audit's job, after a render. This * gate answers the earlier and cheaper question that nothing asked: on an * image-to-video route the keyframe IS the identity lock, so a character listed * on the shot but absent from its start frame gets invented by the model. That * is what produced a shot with tangled extra limbs and the wrong costume, and * no gate looked at it. */ export declare function buildKeyframeCastPrompt(subject: KeyframeCastSubject): string; /** * Scenes whose keyframe is missing a character the shot lists. * * Pure apart from the injected client, so the whole decision is testable with * no network. A client returning `null` (no key, call failed) yields an * ADVISORY, never a pass and never a false failure — an unverified check that * reports success is the failure mode this file already exists to close. */ export declare function findMissingKeyframeCast(scenes: Array<{ sceneIndex: number; keyframePath: string; cast: KeyframeCastSubject[]; }>, client: KeyframeCastVisionClient): Promise; /** * Gemini-Vision client for the cast check. Returns null rather than throwing on * an unreadable image or a failed call, so the gate degrades to an advisory * instead of failing a good keyframe. */ export declare function createKeyframeCastVisionClient(options?: { endpoint?: string; keyOverride?: string; fetcher?: typeof fetch; }): KeyframeCastVisionClient; export interface KeyframeQcOptions { /** * Run the vision cast check. Off by default: it costs one call per listed * character per scene, and the rest of this gate is deterministic and free. */ checkCast?: boolean; /** Injectable for tests; defaults to {@link createKeyframeCastVisionClient}. */ castClient?: KeyframeCastVisionClient; env?: NodeJS.ProcessEnv; } /** * Run the fail-fast pre-render keyframe readiness gate for a project. * Deterministic, read-only, no provider calls. See the module doc for the * finding taxonomy; status is 'fail' iff any 'error' finding is present. */ export declare function runKeyframeQc(workspaceRoot: string, projectSlug: string, options?: KeyframeQcOptions): Promise; //# sourceMappingURL=keyframe-qc.d.ts.map