/** * Reference-transfer contract — a PURE, deterministic role-separation emitter + * advisory. No I/O, no Date, no Math.random. * * Reference channels bleed (model-mechanics #6: "references bleed; always state * what must NOT transfer"; failure-atlas row 11: "Reference roles contaminate — * transfer/ignore clauses were absent"). A multi-reference packet should state, * per reference, what it CONTROLS and what must NOT transfer. This emits that * canonical clause and warns when a multi-domain packet lacks it. * * Harvested from the MIT `Emily2040/seedance-2.0` reference-transfer-contract * (@63b32dc). Advisory by construction — nothing here mutates a live payload. */ export type ReferenceTransferRole = | 'character-sheet' | 'storyboard-grid' | 'start-frame' | 'end-frame' | 'reference-image' | 'background-plate'; export interface ReferenceTransferInput { role: ReferenceTransferRole; label: string; } export interface ReferenceTransferAdvisory { code: 'reference-transfer'; severity: 'warning'; message: string; } /** Bleed-relevant domain per role. `storyboard-grid` -> 'layout' (excluded: it * has the single-full-frame guard and is never an identity/motion donor). */ const ROLE_DOMAIN: Record = { 'character-sheet': 'identity', 'start-frame': 'continuity', 'end-frame': 'continuity', 'background-plate': 'environment', 'reference-image': 'appearance', 'storyboard-grid': 'layout', }; const LAYOUT_DOMAIN = 'layout'; /** What each domain controls and what must not transfer from it. */ const DOMAIN_CONTRACT: Record = { identity: { controls: 'face, hair, wardrobe, and silhouette', ignore: 'background, environment, camera, and lighting', }, continuity: { controls: 'the opening state, pose, and framing', ignore: 'identity, wardrobe colour, and environment design', }, environment: { controls: 'the location, set, and lighting plate', ignore: 'any people, identity, and wardrobe', }, appearance: { controls: "the depicted subject's appearance", ignore: 'background, camera, and motion', }, }; function nonLayoutRefs(refs: ReferenceTransferInput[]): Array { return refs .map((ref) => ({ ...ref, domain: ROLE_DOMAIN[ref.role] })) .filter((ref) => ref.domain !== LAYOUT_DOMAIN); } /** Distinct bleed-relevant domains present (layout excluded), first-appearance order. */ export function referenceDomains(refs: ReferenceTransferInput[]): string[] { const out: string[] = []; for (const ref of nonLayoutRefs(refs)) { if (!out.includes(ref.domain)) out.push(ref.domain); } return out; } /** * The canonical "REFERENCE ROLES:" block — one line per non-layout reference: * "