/** * Allocation-model — a PURE, deterministic over-allocation detector. No I/O, no * Date, no Math.random. * * Identity fidelity, motion boldness, and scene density compete for one * generation's fidelity budget; a shot that demands a perfect face AND a backflip * AND a crowded market AND readable on-screen text lands none of them cleanly * (allocation-model.md). This warns when a single packet over-allocates. * * Conservative by design (low false-positive): it fires only when * ≥ ALLOCATION_DEMAND_THRESHOLD of the four competing demands are present, and the * cue sets are STRICT operator-content phrases that the toolchain's own standing * blocks (SUBJECT LOCK / CAPTURE REALISM / CAMERA CAPTURE / "no on-screen text") * do not contain — so assembled packets never self-trigger. * * Harvested from the MIT `Emily2040/seedance-2.0` allocation-model (@63b32dc). */ export interface AllocationSpend { spend: string; buys: string; strains: string; } /** The three competing spends (reference data, verbatim from the doc). */ export const ALLOCATION_SPENDS: readonly AllocationSpend[] = [ { spend: 'Identity fidelity', buys: 'stable faces, products, logos, costumes', strains: 'motion range — every bold move risks drift' }, { spend: 'Motion boldness', buys: 'committed action, physics, choreography', strains: 'close-up identity detail, especially faces and hands' }, { spend: 'Scene density', buys: 'crowds, layered environments, weather, props', strains: 'per-subject stability; tiny background detail degrades' }, ]; export interface AllocationTrade { ifNeeds: string; paysWith: string; } /** The trade table (reference data). */ export const ALLOCATION_TRADES: readonly AllocationTrade[] = [ { ifNeeds: 'Bold motion and a close-up face', paysWith: 'choose one; put the emotion in posture and staging, or cut to a separate close-up shot' }, { ifNeeds: 'Many subjects', paysWith: 'per-subject identity precision; pick one hero and let the rest read as shapes' }, { ifNeeds: 'Readable on-screen text', paysWith: 'nothing — move text to post' }, { ifNeeds: 'A crowded frame and a tiny product detail', paysWith: 'the detail; isolate the product beat in its own shot' }, ]; export type AllocationDemand = 'identity-detail' | 'bold-motion' | 'scene-density' | 'readable-text'; export const ALLOCATION_DEMAND_THRESHOLD = 3; // Strict operator-content cues, chosen to avoid the standing blocks. Order is the // canonical AllocationDemand order. const DEMAND_CUES: ReadonlyArray = [ ['identity-detail', [ 'extreme close-up', 'extreme closeup', 'macro shot', 'macro lens', 'every pore', 'pore-level', 'tack-sharp face', 'tack-sharp eyes', 'tack-sharp facial', 'individual freckles', 'perfect facial', 'perfect face', 'razor-sharp facial', 'razor-sharp face', 'crisp facial detail', ]], ['bold-motion', [ 'backflip', 'somersault', 'sprint', 'parkour', 'acrobat', 'cartwheel', 'high-speed chase', 'explosion', 'martial arts', 'fight choreography', 'sword fight', 'gunfight', 'fistfight', 'fist fight', 'flips through', 'spins rapidly', 'fast-paced action', 'leaps through', ]], ['scene-density', [ 'crowd', 'crowded', 'bustling', 'throng', 'teeming', 'swarming', 'dozens of people', 'hundreds of people', 'packed with people', 'packed market', 'busy market', 'sea of people', 'background characters', ]], ['readable-text', [ 'readable text', 'legible text', 'text reads', 'sign reads', 'billboard reads', 'label reads', 'clearly readable', 'legible signage', 'readable signage', ]], ]; export interface AllocationAdvisory { code: 'allocation'; severity: 'warning'; message: string; } /** Which competing demands are clearly present (strict cues), canonical order. */ export function detectAllocationDemands(promptText: string): AllocationDemand[] { const lower = promptText.toLowerCase(); const out: AllocationDemand[] = []; for (const [demand, cues] of DEMAND_CUES) { if (cues.some((cue) => lower.includes(cue))) out.push(demand); } return out; } /** * Warn when a single packet collides ≥ ALLOCATION_DEMAND_THRESHOLD competing * demands. Pure: depends only on its argument. */ export function analyzeAllocation(promptText: string): AllocationAdvisory[] { const demands = detectAllocationDemands(promptText); if (demands.length < ALLOCATION_DEMAND_THRESHOLD) return []; return [ { code: 'allocation', severity: 'warning', message: `packet demands ${demands.join(' + ')} in one shot — a single generation can't land all of them cleanly. Name one primary spend, offload identity to references, and split the rest into separate shots.`, }, ]; }